Skip to content

Commit 08aeccd

Browse files
committed
feat: Program の最終成否メッセージのみコンソール色強調を追加
- LoggerService に色指定付き LogMessage オーバーロードを追加(既存挙動は維持) - Program.cs の最終成功メッセージを緑、例外メッセージを赤で表示 - README(ja/en) に色強調ルールを追記 - LoggerService のテストを追加
1 parent 3cc6f3f commit 08aeccd

6 files changed

Lines changed: 135 additions & 4 deletions

File tree

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using System;
2+
using System.IO;
3+
using FolderDiffIL4DotNet.Services;
4+
using Xunit;
5+
6+
namespace FolderDiffIL4DotNet.Tests.Services
7+
{
8+
[Collection("LoggerServiceTests NonParallel")]
9+
public sealed class LoggerServiceTests
10+
{
11+
[Fact]
12+
public void LogMessage_WithExplicitConsoleColor_WritesFormattedMessageAndStackTraceToLogFile()
13+
{
14+
var originalOut = Console.Out;
15+
var originalLogDir = LoggerService._logDirectoryAbsolutePath;
16+
var originalLogFile = LoggerService._logFileAbsolutePath;
17+
var tempDir = Path.Combine(Path.GetTempPath(), "fd-logger-tests-" + Guid.NewGuid().ToString("N"));
18+
Directory.CreateDirectory(tempDir);
19+
var tempLogPath = Path.Combine(tempDir, "log_test.log");
20+
var writer = new StringWriter();
21+
22+
try
23+
{
24+
Console.SetOut(writer);
25+
LoggerService._logDirectoryAbsolutePath = tempDir;
26+
LoggerService._logFileAbsolutePath = tempLogPath;
27+
28+
Exception captured;
29+
try
30+
{
31+
throw new InvalidOperationException("boom");
32+
}
33+
catch (Exception ex)
34+
{
35+
captured = ex;
36+
}
37+
38+
LoggerService.LogMessage(LoggerService.LogLevel.Error, "failure", shouldOutputMessageToConsole: true, ConsoleColor.Red, captured);
39+
40+
var consoleText = writer.ToString();
41+
Assert.Contains("[ERROR] failure", consoleText);
42+
43+
var logText = File.ReadAllText(tempLogPath);
44+
Assert.Contains("[ERROR] failure", logText);
45+
Assert.Contains(captured.StackTrace, logText);
46+
}
47+
finally
48+
{
49+
Console.SetOut(originalOut);
50+
LoggerService._logDirectoryAbsolutePath = originalLogDir;
51+
LoggerService._logFileAbsolutePath = originalLogFile;
52+
try
53+
{
54+
if (Directory.Exists(tempDir))
55+
{
56+
Directory.Delete(tempDir, recursive: true);
57+
}
58+
}
59+
catch
60+
{
61+
// ignore cleanup errors in tests
62+
}
63+
}
64+
}
65+
66+
[Fact]
67+
public void LogMessage_DefaultOverload_KeepsExistingConsoleFormat()
68+
{
69+
var originalOut = Console.Out;
70+
var originalLogFile = LoggerService._logFileAbsolutePath;
71+
var writer = new StringWriter();
72+
73+
try
74+
{
75+
Console.SetOut(writer);
76+
LoggerService._logFileAbsolutePath = null;
77+
78+
LoggerService.LogMessage(LoggerService.LogLevel.Info, "success", shouldOutputMessageToConsole: true);
79+
80+
var consoleText = writer.ToString();
81+
Assert.Contains("[INFO] success", consoleText);
82+
}
83+
finally
84+
{
85+
Console.SetOut(originalOut);
86+
LoggerService._logFileAbsolutePath = originalLogFile;
87+
}
88+
}
89+
}
90+
91+
[CollectionDefinition("LoggerServiceTests NonParallel", DisableParallelization = true)]
92+
public sealed class LoggerServiceTestCollectionDefinition
93+
{
94+
}
95+
}

Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,12 +308,12 @@ static async Task<int> Main(string[] args)
308308
_config);
309309

310310
// 正常終了メッセージ出力
311-
LoggerService.LogMessage(LoggerService.LogLevel.Info, LOG_APP_FINISHED, shouldOutputMessageToConsole: true);
311+
LoggerService.LogMessage(LoggerService.LogLevel.Info, LOG_APP_FINISHED, shouldOutputMessageToConsole: true, ConsoleColor.Green);
312312
}
313313
catch (Exception ex)
314314
{
315315
// 例外を捕捉した場合は、stacktraceをログに出力して終了
316-
LoggerService.LogMessage(LoggerService.LogLevel.Error, ex.Message, shouldOutputMessageToConsole: true, ex);
316+
LoggerService.LogMessage(LoggerService.LogLevel.Error, ex.Message, shouldOutputMessageToConsole: true, ConsoleColor.Red, ex);
317317
LoggerService.LogMessage(LoggerService.LogLevel.Info, string.Format(LOG_ERROR_DETAILS_PATH, LoggerService._logFileAbsolutePath), shouldOutputMessageToConsole: true);
318318
exitCode = EXIT_CODE_ERROR;
319319
}

README.en.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ dotnet run "/Users/UserA/workspace/old" "/Users/UserA/workspace/new" "YYYYMMDD"
223223
```
224224

225225
The console shows progress, and after completion the report is available at `Reports/<label>/diff_report.md`.
226+
Console color emphasis is intentionally minimal: only the final success message (`[INFO] ... finished without errors`) is shown in green, and only the exception error message (`[ERROR] ...`) is shown in red (all other logs use the default color).
226227

227228
After writing the report, the following files are marked read-only (failures only generate warnings):
228229

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ dotnet run "/Users/UserA/workspace/old" "/Users/UserA/workspace/new" "YYYYMMDD"
241241
```
242242

243243
実行するとコンソールに進捗率が表示され、完了後`Reports/<コマンドライン第3引数に指定したレポートのラベル>/diff_report.md`にレポートが生成されます。
244+
コンソール強調表示は最小限で、最終の成功メッセージ(`[INFO] ... finished without errors`)のみ緑、例外発生時のエラーメッセージ(`[ERROR] ...`)のみ赤で表示されます(それ以外は既定色)。
244245

245246
出力完了後、以下の生成物は読み取り専用(ReadOnly 属性)に変更されます(失敗時は警告を出し処理は継続)。
246247
- `diff_report.md`

Services/LoggerService.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,23 @@ public static void Initialize()
112112
/// <exception cref="DirectoryNotFoundException">ログディレクトリが存在しない、またはパスが無効な場合。</exception>
113113
/// <exception cref="IOException">ファイル書き込み時に I/O エラーが発生した場合。</exception>
114114
public static void LogMessage(LogLevel logLevel, string message, bool shouldOutputMessageToConsole, Exception exception = null)
115+
=> LogMessage(logLevel, message, shouldOutputMessageToConsole, consoleForegroundColor: null, exception);
116+
117+
/// <summary>
118+
/// メッセージをログファイルに追記し、必要に応じてコンソールにも指定色で出力します。
119+
/// <para>
120+
/// まだ <see cref="Initialize"/> が呼ばれていない場合は、コンソール出力(指定時)のみ行い、ファイル出力はスキップします。
121+
/// </para>
122+
/// </summary>
123+
/// <param name="logLevel">ログレベル。</param>
124+
/// <param name="message">出力するメッセージ(null 可)。</param>
125+
/// <param name="shouldOutputMessageToConsole">true の場合、メッセージをコンソールにも出力します。</param>
126+
/// <param name="consoleForegroundColor">コンソール出力に適用する文字色(未指定時は既定色)。</param>
127+
/// <param name="exception">例外情報(省略可)。指定した場合、スタックトレースをログファイルに追記します。</param>
128+
/// <exception cref="UnauthorizedAccessException">ログファイルへの書き込み権限がない場合。</exception>
129+
/// <exception cref="DirectoryNotFoundException">ログディレクトリが存在しない、またはパスが無効な場合。</exception>
130+
/// <exception cref="IOException">ファイル書き込み時に I/O エラーが発生した場合。</exception>
131+
public static void LogMessage(LogLevel logLevel, string message, bool shouldOutputMessageToConsole, ConsoleColor? consoleForegroundColor, Exception exception = null)
115132
{
116133
string formattedMessage = FormatMessage(message, logLevel);
117134

@@ -135,7 +152,24 @@ public static void LogMessage(LogLevel logLevel, string message, bool shouldOutp
135152
// ignore console position errors
136153
}
137154
}
138-
Console.WriteLine(formattedMessage);
155+
156+
if (consoleForegroundColor.HasValue && !Console.IsOutputRedirected)
157+
{
158+
var originalColor = Console.ForegroundColor;
159+
try
160+
{
161+
Console.ForegroundColor = consoleForegroundColor.Value;
162+
Console.WriteLine(formattedMessage);
163+
}
164+
finally
165+
{
166+
Console.ForegroundColor = originalColor;
167+
}
168+
}
169+
else
170+
{
171+
Console.WriteLine(formattedMessage);
172+
}
139173
}
140174

141175
// 初期化前の場合はコンソール出力のみで終了。

version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
3-
"version": "1.1.8",
3+
"version": "1.1.9",
44
"publicReleaseRefSpec": [
55
"^refs/heads/main$",
66
"^refs/tags/v\\d+\\.\\d+(?:\\.\\d+)?$"

0 commit comments

Comments
 (0)