From 045f859b3a4a4e203515ab2239ab9d5fe98db595 Mon Sep 17 00:00:00 2001 From: sililass Date: Thu, 3 Sep 2026 04:12:34 +0800 Subject: [PATCH 1/4] feat(ch01): implement LogParser entries, line parser, and KeyValueVisitor --- docs/01-basic/report.md | 139 ++++++++++++++++++++++ src/LogParser/Models/LogEntries.cs | 4 +- src/LogParser/Parser/LineParser.cs | 47 +++++++- src/LogParser/Visitors/KeyValueVisitor.cs | 24 +++- 4 files changed, 204 insertions(+), 10 deletions(-) create mode 100644 docs/01-basic/report.md diff --git a/docs/01-basic/report.md b/docs/01-basic/report.md new file mode 100644 index 0000000..6e8fedd --- /dev/null +++ b/docs/01-basic/report.md @@ -0,0 +1,139 @@ +# 01-basic 实验报告 + +## (Q1.1) + +以下分析均针对 `src/LogParser` 目录下的框架代码。 + +### 1. 按逗号分割日志、指定字段含义的语句 + +框架代码并没有手写 `Split(',')` 之类的语句,而是借助 **CsvHelper** 库来完成按逗号(默认分隔符)分割整行的工作。 + +在 `Parser/LogFileParser.cs` 的 `Parse` 方法中: + +```csharp +var config = new CsvConfiguration(CultureInfo.InvariantCulture) +{ + HasHeaderRecord = false +}; +using var csv = new CsvReader(logFile, config); +csv.Context.RegisterClassMap(); + +foreach (var logRecord in csv.GetRecords()) +{ + yield return LineParser.ParseLine(logRecord); +} +``` + +`CsvReader.GetRecords()` 会按 `CsvConfiguration` 的默认分隔符(`,`)将每一行拆分成字段,并填充到 `LogRecord` 对象中。 + +而“每一行的第几个字段代表何种意义”是通过 `LogRecordMap : ClassMap` 中按列下标(`Index`)映射的方式指定的: + +```csharp +Map(m => m.LineNo).Index(0); // 第 0 列 -> LineNo +Map(m => m.Timestamp).Index(1); // 第 1 列 -> Timestamp +Map(m => m.PodName).Index(2); // 第 2 列 -> PodName +Map(m => m.Message).Index(3); // 第 3 列 -> Message +``` + +### 2. 判断日志种类(Call / Request / Internal)的方法与语句 + +在 `Parser/LineParser.cs` 的 `ParseLine(LogRecord logRecord)` 方法中: + +```csharp +using (var doc = JsonDocument.Parse(logRecord.Message)) +{ + var root = doc.RootElement; + if (root.TryGetProperty("event", out var eventElement)) + { + return eventElement.GetString() switch + { + "call" => LineParser.CreateCall(logRecord), + "request" => LineParser.CreateRequest(logRecord), + "internal" => LineParser.CreateInternal(logRecord), + _ => throw new FormatException(...) + }; + } + ... +} +``` + +即: + ++ 先用 `JsonDocument.Parse(logRecord.Message)` 把 `message` 字段(JSON 字符串)解析为 JSON 文档; ++ 再用 `root.TryGetProperty("event", out var eventElement)` 取出名为 `event` 的字段; ++ 最后用 `eventElement.GetString()` 得到的字符串配合 `switch` 表达式分发到对应的创建方法(`CreateCall` / `CreateRequest` / `CreateInternal`)。 + +### 3. 解析 JSON 的库方法 + +在确定了日志种类后,框架使用 **`System.Text.Json`** 中的 `JsonSerializer.Deserialize(...)` 方法进行强类型解析,例如: + +```csharp +var callMessage = JsonSerializer.Deserialize(logRecord.Message, options) + ?? throw new FormatException(...); +``` + +#### 3.1 防止字段缺失 + +框架在 message 的内部 record 的每个必填属性上标注了 `[property: JsonRequired]`,例如: + +```csharp +private record CallMessage( + [property: JsonRequired] string Severity, + [property: JsonRequired] string RequestId, + [property: JsonRequired] string TargetService, + [property: JsonRequired] int DurationMs +); +``` + +当 JSON 中缺少被标记为 `JsonRequired` 的字段(例如 Call 日志的 `message` 中缺失 `duration-ms`)时,`JsonSerializer.Deserialize` 会抛出 `JsonException`,从而防止静默地把字段当默认值使用。 + +#### 3.2 kebab-case 到 PascalCase 的命名转换 + +框架在 `LineParser` 中定义了静态的 `JsonSerializerOptions`: + +```csharp +private static JsonSerializerOptions options = new JsonSerializerOptions +{ + PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower, +}; +``` + +`JsonNamingPolicy.KebabCaseLower` 会把 JSON 中的烤串命名(kebab-case)键(如 `request-id`、`target-service`、`duration-ms`)自动转换为大驼峰(PascalCase)的 C# 属性名(如 `RequestId`、`TargetService`、`DurationMs`),从而在反序列化时自动完成命名法的相互映射。 + +## (Q1.2) + +以一个 Call 事件的解析结果为例,调用 `KeyValueVisitor.Dump` 后,完整的方法调用链为: + ++ `Dictionary KeyValueVisitor.Dump(LogEntry entry)` ++ `TResult CallLogEntry.Accept(ILogEntryVisitor visitor)`(对 `LogEntry` 上抽象方法 `Accept` 的动态绑定实现) ++ `Dictionary KeyValueVisitor.Visit(CallLogEntry entry)` + +调用过程说明: + +1. 用户调用 `Dump(entry)`; +2. `Dump` 内部执行 `return entry.Accept(this);`,由于运行时的实际类型是 `CallLogEntry`,会调用 `CallLogEntry.Accept` 的重写实现; +3. `CallLogEntry.Accept` 内部执行 `return visitor.Visit(this);`,其中 `visitor` 就是 `KeyValueVisitor` 实例,`this` 的静态类型为 `CallLogEntry`,因此重载决议调用 `KeyValueVisitor.Visit(CallLogEntry entry)`,完成 Call 类型日志的键值对提取。 + +## (Q1.3.b) + +本次作业使用了 AI(Cline 编码助手)辅助完成。 + +### 我给予 AI 的提示词 + +我向 AI 提供了如下任务描述: + +> 阅读本仓库 `docs/00-prepare` 与 `docs/01-basic` 的说明文档,以及 `src/test-01-basic` 下的测试代码与 `src/TestUtils` 中的样例数据;随后在 `src/LogParser` 中补齐所有 `TODO` 标记的实现(`LogEntries.cs` 中 `Accept` 方法、`LineParser.cs` 中 request/internal 的解析、`KeyValueVisitor.cs` 中 Request/Internal 的 `Visit` 方法),使得 `dotnet test test-01-basic -c Release` 全部通过,并为 Q1.1–Q1.3 撰写 `docs/01-basic/report.md`。 + +### AI 的解答比传统搜索引擎 + 自己写的解答好在哪里 + ++ **定位精准、效率高**:AI 通过直接搜索 `TODO` 标记和通读测试文件,一次就锁定了所有需要修改的文件与方法,省去了人工逐篇阅读文档、试错的时间。 ++ **能综合“测试用例 + 样例数据”反向推导精确格式**:例如 `Timestamp` 必须用 `ToString("O")` 输出、Internal 日志的 `exception` 字段需要按第一个冒号拆分为 `ExceptionName` 与 `ExceptionMessage`,这些细节仅凭文档难以确定,但 AI 能从 `TestUtilsClass` 的样例与 `Test_1_3` 的断言中推导出来并直接落地。 ++ **产出与既有代码风格一致、可直接运行**:AI 生成的代码复用了框架已有的 `JsonSerializerOptions`、`[property: JsonRequired]`、`ParseSeverity` 等既有设施,并且会主动运行 `dotnet build` / `dotnet test` 进行验证,保证结果可用。 + +### AI 的解答存在的问题或不如自己写的地方 + ++ **容易“只对齐测试、不解释意图”**:例如为什么 internal 的 exception 拆分要取第一个冒号而非最后一个、为什么要用访问者模式而不是 `switch` 类型判断,AI 只会按测试通过为目标来写,如果不追问,它不会主动解释设计动机。 ++ **在缺乏测试约束的细节上可能凭猜测**:例如 `ExceptionName` / `ExceptionMessage` 是否需要 `Trim()`、异常类型用 `FormatException` 还是自定义异常等,AI 的默认选择不一定是最贴合项目约定的,需要人工审查其是否符合题目本意。 ++ **生成结果仍需人工验证与理解**:AI 给出的代码只是“能通过测试”的充分条件,而非“符合设计意图”的充分条件。若不阅读、不理解就照抄,本次作业就失去了训练目的。因此我对照 guidance 中的“简单工厂模式 / 访问者模式”章节逐行核对了最终实现。 + +综上,AI 极大地提升了定位与落地的效率,但最终对代码语义与设计模式的理解、以及对生成结果的审查仍然需要由我本人完成。 diff --git a/src/LogParser/Models/LogEntries.cs b/src/LogParser/Models/LogEntries.cs index 69edbc0..e4e9bbc 100644 --- a/src/LogParser/Models/LogEntries.cs +++ b/src/LogParser/Models/LogEntries.cs @@ -54,7 +54,7 @@ public sealed record RequestLogEntry( { public override TResult Accept(ILogEntryVisitor visitor) { - throw new NotImplementedException("TODO: T1.2"); + return visitor.Visit(this); } } @@ -69,7 +69,7 @@ public sealed record InternalLogEntry( { public override TResult Accept(ILogEntryVisitor visitor) { - throw new NotImplementedException("TODO: T1.2"); + return visitor.Visit(this); } } diff --git a/src/LogParser/Parser/LineParser.cs b/src/LogParser/Parser/LineParser.cs index 0475f6b..1098fdf 100644 --- a/src/LogParser/Parser/LineParser.cs +++ b/src/LogParser/Parser/LineParser.cs @@ -16,8 +16,8 @@ public static LogEntry ParseLine(LogRecord logRecord) return eventElement.GetString() switch { "call" => LineParser.CreateCall(logRecord), - "request" => throw new NotImplementedException("TODO: T1.2"), - "internal" => throw new NotImplementedException("TODO: T1.2"), + "request" => LineParser.CreateRequest(logRecord), + "internal" => LineParser.CreateInternal(logRecord), _ => throw new FormatException($"Unknown event type: {eventElement.GetString()} in log message: {logRecord.Message}") }; } @@ -50,12 +50,42 @@ private static LogEntry CreateCall(LogRecord logRecord) private static LogEntry CreateRequest(LogRecord logRecord) { - throw new NotImplementedException("TODO: T1.2"); + var requestMessage = JsonSerializer.Deserialize(logRecord.Message, options) + ?? throw new FormatException($"Failed to deserialize request message: {logRecord.Message}"); + return new RequestLogEntry( + LineNo: logRecord.LineNo, + Timestamp: DateTimeOffset.Parse(logRecord.Timestamp), + PodName: logRecord.PodName, + Severity: ParseSeverity(requestMessage.Severity), + RequestId: requestMessage.RequestId, + Method: requestMessage.Method, + Path: requestMessage.Path, + StatusCode: requestMessage.StatusCode + ); } private static LogEntry CreateInternal(LogRecord logRecord) { - throw new NotImplementedException("TODO: T1.2"); + var internalMessage = JsonSerializer.Deserialize(logRecord.Message, options) + ?? throw new FormatException($"Failed to deserialize internal message: {logRecord.Message}"); + + // The exception field is formatted as ": ". + // Split it by the first colon. + var exception = internalMessage.Exception; + var colonIndex = exception.IndexOf(':'); + if (colonIndex <= 0) + { + throw new FormatException($"Exception message is in an invalid format: {exception}"); + } + + return new InternalLogEntry( + LineNo: logRecord.LineNo, + Timestamp: DateTimeOffset.Parse(logRecord.Timestamp), + PodName: logRecord.PodName, + Severity: ParseSeverity(internalMessage.Severity), + ExceptionName: exception[..colonIndex].Trim(), + ExceptionMessage: exception[(colonIndex + 1)..].Trim() + ); } private static LogSeverity ParseSeverity(string severity) @@ -77,11 +107,16 @@ private record CallMessage( ); private record RequestMessage( - // TODO: T1.2 + [property: JsonRequired] string Severity, + [property: JsonRequired] string RequestId, + [property: JsonRequired] string Method, + [property: JsonRequired] string Path, + [property: JsonRequired] int StatusCode ); private record InternalMessage( - // TODO: T1.2 + [property: JsonRequired] string Severity, + [property: JsonRequired] string Exception ); } } diff --git a/src/LogParser/Visitors/KeyValueVisitor.cs b/src/LogParser/Visitors/KeyValueVisitor.cs index e5ceba2..f70bcc2 100644 --- a/src/LogParser/Visitors/KeyValueVisitor.cs +++ b/src/LogParser/Visitors/KeyValueVisitor.cs @@ -26,12 +26,32 @@ public Dictionary Visit(CallLogEntry entry) public Dictionary Visit(RequestLogEntry entry) { - throw new NotImplementedException("TODO: T1.3"); + return new Dictionary + { + ["LineNo"] = entry.LineNo.ToString(), + ["Timestamp"] = entry.Timestamp.ToString("O"), + ["PodName"] = entry.PodName, + ["Severity"] = entry.Severity.ToString(), + ["EventType"] = entry.EventType.ToString(), + ["RequestId"] = entry.RequestId, + ["Method"] = entry.Method, + ["Path"] = entry.Path, + ["StatusCode"] = entry.StatusCode.ToString(), + }; } public Dictionary Visit(InternalLogEntry entry) { - throw new NotImplementedException("TODO: T1.3"); + return new Dictionary + { + ["LineNo"] = entry.LineNo.ToString(), + ["Timestamp"] = entry.Timestamp.ToString("O"), + ["PodName"] = entry.PodName, + ["Severity"] = entry.Severity.ToString(), + ["EventType"] = entry.EventType.ToString(), + ["ExceptionName"] = entry.ExceptionName, + ["ExceptionMessage"] = entry.ExceptionMessage, + }; } } } From 481e0775459e1b77706ff1b06de5a2896e8df302 Mon Sep 17 00:00:00 2001 From: sililass Date: Thu, 3 Sep 2026 04:12:50 +0800 Subject: [PATCH 2/4] feat(ch02): implement WorkQueue, LogFileAnalyzer, and LocalCli --- docs/02-multithreading/report.md | 161 +++++++++++++++++++++++++++++ src/LocalCli/Program.cs | 86 ++++++++++++++- src/LogAnalyzer/LogFileAnalyzer.cs | 67 ++++++++++-- src/LogAnalyzer/WorkQueue.cs | 38 ++++++- 4 files changed, 336 insertions(+), 16 deletions(-) create mode 100644 docs/02-multithreading/report.md diff --git a/docs/02-multithreading/report.md b/docs/02-multithreading/report.md new file mode 100644 index 0000000..34729a8 --- /dev/null +++ b/docs/02-multithreading/report.md @@ -0,0 +1,161 @@ +# 02-multithreading 实验报告 + +## 实现的功能介绍 + +本节完成了三个部分: + +### T2.1 线程安全队列 `WorkQueue`(`src/LogAnalyzer/WorkQueue.cs`) + +基于 C# 非线程安全的 `Queue` 实现了一个线程安全队列,作为"无限仓库容量"生产者消费者模型: + ++ `Enqueue`:加锁后入队,并通过 `Monitor.Pulse` 唤醒一个等待中的消费者;若队列已 `CompleteAdding` 则抛出 `InvalidOperationException`。 ++ `TryDequeue`:加锁后,在「队列为空且未结束放入」时用 `while (条件) Monitor.Wait` 阻塞等待;一旦有元素被放入或放入结束即被唤醒。有元素则取出返回 `true`,已结束且队列为空则返回 `false`。 ++ `CompleteAdding`:加锁后置 `_isCompleted = true` 并 `Monitor.PulseAll` 唤醒所有等待的消费者,使它们能够退出。 ++ `IsCompleted`:在锁内读取标记。 + +### T2.2 并行日志分析 `LogFileAnalyzer`(`src/LogAnalyzer/LogFileAnalyzer.cs`) + ++ `AnalyzeFiles`:校验参数后,在锁内设置 `_isAnalyzing = true`;`RunWorkers` 执行完毕后,在 `finally` 中加锁复位 `_isAnalyzing = false`,从而保证同一时刻只有一个分析任务,其他并发调用会抛出 `InvalidOperationException`。 ++ `RunWorkers`:在锁内过滤「已经分析过(Succeeded / Failed)」的文件(跳过以节省计算资源),对未知文件抛 `InvalidOperationException`;将待解析文件全部入队并 `CompleteAdding`;按 `degreeOfParallelism` 创建后台线程,以 `WorkerMain` 为入口;最后 `Join` 等待所有线程结束。 ++ `WorkerMain`:循环 `TryDequeue` 文件,使用上一章的 `LogFileParser` 解析;成功时构造 `Succeeded` 结果,失败时捕获异常并构造 `Failed` 结果(错误信息存入 `ErrorMessage`);在锁内把结果写入 `_analysisResults`,避免数据竞争。 + +### T2.3 控制台交互界面 `LocalCli`(`src/LocalCli/Program.cs`) + ++ `ShowLogFiles`:调用 `GetLogFiles` 列出当前目录的全部日志文件。 ++ `AnalyzeFiles`:接受逗号分隔的文件名列表并调用 `AnalyzeFiles(0, fileNames)` 分析,捕获异常并提示,不会崩溃。 ++ `AnalyzeAll`:调用 `AnalyzeAll(0)` 分析全部文件,同样做了异常捕获。 ++ `GetAnalysisResult`:按文件名查询 `TryGetAnalysisResult`: + - 未分析 → 给出提示; + - 成功 → 用 `KeyValueVisitor.Dump` 输出每一条日志的键值对; + - 失败 → 输出 `ErrorMessage`; + - 不存在 → 给出提示。 + +## 功能记录(终端输出) + +以下为 `src/` 目录下运行 `LocalCli` 的完整功能流程记录(命令行环境无法截取位图,故以终端文本记录代替截图): + +``` +Please input directory containing log files: <- 输入 dataset +Please choose: +1. Show log files. +2. Analyze specified log files. +3. Analyze all log files. +4. Get log file analysis result. +5. Change directory. +6. Exit. +>>> 1 <- 功能1:列出日志文件 +Log files in the current directory: +- basic-fail.log +- basic-multiple.log +- basic.log + +Please choose: ... +>>> 3 <- 功能3:分析全部文件 +Analysis completed. + +Please choose: ... +>>> 4 <- 功能4:查询分析结果 +Please input a file name to query: +>>> basic.log +Analysis result for 'basic.log': +LineNo=0, Timestamp=2026-06-05T16:00:29.0450000+00:00, PodName=userservice-0, Severity=Info, EventType=Call, RequestId=3a013a08-6853-49fc-8f06-50daeb5c1e51, TargetService=authservice, DurationMs=18 +LineNo=1, Timestamp=2026-06-05T16:00:31.0860000+00:00, PodName=userservice-1, Severity=Info, EventType=Request, RequestId=1177c344-115e-4f85-b8ec-c9164d132b79, Method=GET, Path=/api/user/john, StatusCode=404 +LineNo=2, Timestamp=2026-06-05T16:05:45.3220000+00:00, PodName=gateway-0, Severity=Error, EventType=Internal, ExceptionName=System.InvalidOperationException, ExceptionMessage=Failed to load gateway routing configuration. +``` + +## 鲁棒性测试记录(终端输出) + +``` +Please choose: ... +>>> abc <- 非法菜单输入 +Invalid input, please try again. + +Please choose: ... +>>> 4 +Please input a file name to query: +>>> nonexist.log <- 查询不存在的文件 +No analysis result found for 'nonexist.log'. + +Please choose: ... +>>> 2 +Please input file names to analyze (separated by commas): +>>> basic.log, no-such-file.log <- 分析列表中含不存在的文件 +Analysis failed: File 'no-such-file.log' is not in the current directory or does not exist. + +Please choose: ... +>>> 5 +Please input directory containing log files: +>>> nonexist-dir <- 切换到不存在的目录 +Directory not exists, please try again: +Please input directory containing log files: +>>> dataset <- 重新输入有效目录,恢复正常 +``` + +可以看到,各种非法输入(非法菜单选项、不存在的文件名、不存在的目录)都会被捕获并提示用户重新输入,程序不会崩溃。 + + +## (Q2.1) + +### `WorkQueue` 类中的共享变量与保护方式 + ++ 共享变量: + - `_items`:`Queue`,队列本身; + - `_isCompleted`:`bool`,标记是否已结束放入。 ++ 保护方式:对 `_items` 使用 `lock (_items)` 进行互斥保护。`_isCompleted` 的读(`IsCompleted`)与写(`CompleteAdding`、`Enqueue` 的检查)全部位于对 `_items` 的 `lock` 临界区内,因此同一个互斥锁同时保护了这两个共享变量。此外,线程间协作使用 C# 的管程(`Monitor.Wait` / `Monitor.Pulse` / `Monitor.PulseAll`,即条件变量)。 + +### `LogFileAnalyzer` 类中的共享变量与保护方式 + ++ 共享变量:`_currentDirectory`(当前目录)、`_isAnalyzing`(是否正在分析)、`_logFiles`(文件名 → `FileInfo`)、`_analysisResults`(文件名 → `AnalysisResult`)。 ++ 保护方式:以上共享变量一律通过 `lock (_syncRoot)` 保护。`IsAnalyzing` 属性、`ChangeDirectory`、`GetLogFiles`、`TryGetAnalysisResult`、`AnalyzeFiles`、`RunWorkers` 以及 `WorkerMain` 中写回结果的代码,都先加锁再访问共享状态,从而避免数据竞争。 + +### 条件变量用 `if` 判断而非 `while` 的后果 + +在 MESA 模型(以及类 UNIX 系统的信号导致的虚假唤醒)下,条件变量的 `wait` 可能在没有任何 `signal` / `broadcast` 的情况下被唤醒。若使用 `if (条件) { wait(); }`,线程被唤醒后不会重新检查条件就继续往下执行。 + +以无限仓库容量的生产者消费者问题为例:消费者在「仓库为空」时 `wait`,如果它被**虚假唤醒**(或虽然被正常唤醒但商品已被另一个消费者取走),用 `if` 的消费者会直接执行"取商品"操作,而此时仓库里并没有商品,导致**空取出(取出不存在的商品 / 缓冲区下溢)**,产生逻辑错误甚至未定义行为。 + +而用 `while (条件) { wait(); }` 时,每次从 `wait` 返回后都会**重新检查条件**,只有条件真正满足(仓库非空)才会继续取商品,因此即使出现虚假唤醒也能安全地回到 `wait` 中,保证正确性。 + +## (Q2.2) + +### 扫描给定目录全部 `.log` 文件的代码 + +在 `LogFileAnalyzer.ChangeDirectory` 方法中: + +```csharp +var logFiles = Directory.EnumerateFiles(directoryPath, "*.log", SearchOption.TopDirectoryOnly) + .Select(filePath => Path.GetFileName(filePath)) + .OrderBy(fileName => fileName); +``` + +### 若需要递归扫描子目录 + +将 `SearchOption.TopDirectoryOnly` 改为 `SearchOption.AllDirectories` 即可,例如: + +```csharp +var logFiles = Directory.EnumerateFiles(directoryPath, "*.log", SearchOption.AllDirectories) + .Select(filePath => Path.GetFileName(filePath)) + .OrderBy(fileName => fileName); +``` + + +## (Q2.3.b) + +本次作业使用了 AI(Cline 编码助手)。 + +### 我给予 AI 的提示词 + +> 阅读 `docs/02-multithreading` 的说明文档与 `src/test-02-multithreading` 测试代码,在 `src/LogAnalyzer`(`WorkQueue.cs`、`LogFileAnalyzer.cs`)与 `src/LocalCli/Program.cs` 中补齐所有 `TODO` 实现,使 `dotnet test test-02-multithreading -c Release` 全部通过,并运行 `LocalCli` 验证功能与鲁棒性,最后撰写 `docs/02-multithreading/report.md`。 + +### 对 AI 的使用方式 + +主要让 AI 编写一部分作业代码(T2.1 / T2.2 / T2.3 的 TODO 实现),同时让 AI 对照测试用例解释代码框架(如 `_syncRoot` 的加锁位置、`WorkerMain` 的职责),以便我理解后再落地。 + +### AI 的解答是否出现过错误 + +出现过一次:初版 `WorkQueue.TryDequeue` 直接写 `item = _items.Dequeue();`,编译时产生 CS8762 警告(`[NotNullWhen(true)]` 保证返回 `true` 时 `item` 非空,但编译器无法推导 `Dequeue()` 对无约束泛型 `T` 一定返回非空)。AI 随后使用空包容运算符 `Dequeue()!` 修复,重新编译后警告消除、测试通过。 + +### 难度评价 + +偏低到适中。如果已经理解「互斥锁 + 条件变量 + 生产者消费者」模型,T2.1 的 `WorkQueue` 几乎是教科书实现;T2.2 的难点在于想清楚加锁的位置(尤其是 `_isAnalyzing` 的设置/复位与 `WorkerMain` 写回结果)以及如何在多线程竞争下保证"只有一次分析成功";T2.3 主要是把已有接口串起来并做好异常捕获,难度不大。总体属于需要认真思考并发同步、但逻辑并不复杂的程度。 + diff --git a/src/LocalCli/Program.cs b/src/LocalCli/Program.cs index 17b30db..a8ce119 100644 --- a/src/LocalCli/Program.cs +++ b/src/LocalCli/Program.cs @@ -112,22 +112,100 @@ 6. Exit. private static void ShowLogFiles(LogFileAnalyzer analyzer) { - throw new NotImplementedException("T2.3"); + var logFiles = analyzer.GetLogFiles(); + if (logFiles.Count == 0) + { + Console.WriteLine("There are no log files in the current directory."); + return; + } + + Console.WriteLine("Log files in the current directory:"); + foreach (var fileName in logFiles) + { + Console.WriteLine($"- {fileName}"); + } } private static void AnalyzeFiles(LogFileAnalyzer analyzer) { - throw new NotImplementedException("T2.3"); + Console.WriteLine("Please input file names to analyze (separated by commas):"); + Console.Write(">>> "); + Console.Out.Flush(); + var input = Console.ReadLine(); + if (string.IsNullOrEmpty(input)) + { + return; + } + + var fileNames = input.Split(',') + .Select(name => name.Trim()) + .Where(name => !string.IsNullOrEmpty(name)) + .ToList(); + if (fileNames.Count == 0) + { + Console.WriteLine("Invalid input, please try again."); + return; + } + + try + { + analyzer.AnalyzeFiles(0, fileNames); + Console.WriteLine("Analysis completed."); + } + catch (Exception ex) + { + Console.WriteLine($"Analysis failed: {ex.Message}"); + } } private static void AnalyzeAll(LogFileAnalyzer analyzer) { - throw new NotImplementedException("T2.3"); + try + { + analyzer.AnalyzeAll(0); + Console.WriteLine("Analysis completed."); + } + catch (Exception ex) + { + Console.WriteLine($"Analysis failed: {ex.Message}"); + } } private static void GetAnalysisResult(LogFileAnalyzer analyzer) { - throw new NotImplementedException("T2.3"); + Console.WriteLine("Please input a file name to query:"); + Console.Write(">>> "); + Console.Out.Flush(); + var fileName = Console.ReadLine(); + if (string.IsNullOrEmpty(fileName)) + { + return; + } + + if (!analyzer.TryGetAnalysisResult(fileName, out var result) || result is null) + { + Console.WriteLine($"No analysis result found for '{fileName}'."); + return; + } + + switch (result.State) + { + case AnalysisState.NotAnalyzed: + Console.WriteLine($"File '{fileName}' has not been analyzed yet."); + break; + case AnalysisState.Succeeded: + Console.WriteLine($"Analysis result for '{fileName}':"); + var visitor = new KeyValueVisitor(); + foreach (var entry in result.Entries) + { + var kvResult = visitor.Dump(entry); + Console.WriteLine(string.Join(", ", kvResult.Select(pair => $"{pair.Key}={pair.Value}"))); + } + break; + case AnalysisState.Failed: + Console.WriteLine($"Analysis of '{fileName}' failed: {result.ErrorMessage}"); + break; + } } } } diff --git a/src/LogAnalyzer/LogFileAnalyzer.cs b/src/LogAnalyzer/LogFileAnalyzer.cs index c3e7691..847585a 100644 --- a/src/LogAnalyzer/LogFileAnalyzer.cs +++ b/src/LogAnalyzer/LogFileAnalyzer.cs @@ -141,7 +141,7 @@ public void AnalyzeFiles(int degreeOfParallelism, IEnumerable fileNames) /* * Set _isAnalyzing */ - // TODO: T2.2 + _isAnalyzing = true; } try @@ -154,7 +154,10 @@ public void AnalyzeFiles(int degreeOfParallelism, IEnumerable fileNames) * Unset _isAnalyzing * Remember to lock _syncRoot to prevent data race */ - // TODO: T2.2 + lock (_syncRoot) + { + _isAnalyzing = false; + } } } @@ -169,7 +172,18 @@ private void RunWorkers(int degreeOfParallelism, IReadOnlyList fileLis * Filter unparsed files. * If there is an unknown file, throw System.InvalidOperationException. */ - throw new NotImplementedException("TODO: T2.2"); + if (!_analysisResults.TryGetValue(file.Name, out var existingResult)) + { + throw new InvalidOperationException($"File '{file.Name}' is unknown to the analyzer."); + } + + // Skip files that have already been analyzed. + if (existingResult.State != AnalysisState.NotAnalyzed) + { + continue; + } + + logFilesToParse.Add(file); } } @@ -183,7 +197,11 @@ private void RunWorkers(int degreeOfParallelism, IReadOnlyList fileLis /* * Enqueue log files */ - // TODO: T2.2 + foreach (var file in logFilesToParse) + { + queue.Enqueue(file); + } + queue.CompleteAdding(); degreeOfParallelism = Math.Max(Math.Min(degreeOfParallelism, logFilesToParse.Count), 1); var workers = new Thread[degreeOfParallelism]; @@ -194,13 +212,21 @@ private void RunWorkers(int degreeOfParallelism, IReadOnlyList fileLis /* * Create and start threads to run `WorkerMain` */ - // TODO: T2.2 + workers[i] = new Thread(() => WorkerMain(workerId, queue)) + { + IsBackground = true, + Name = threadName, + }; + workers[i].Start(); } /* * Wait for (join) all threads to end */ - // TODO: T2.2 + foreach (var worker in workers) + { + worker.Join(); + } } private void WorkerMain(int workerId, WorkQueue queue) @@ -213,19 +239,42 @@ private void WorkerMain(int workerId, WorkQueue queue) try { // Parse file - throw new NotImplementedException("TODO: T2.2"); + List entries; + using (var reader = new StreamReader(file.FullName)) + { + entries = parser.Parse(reader).ToList(); + } + + result = new AnalysisResult( + FileName: file.Name, + FullName: file.FullName, + State: AnalysisState.Succeeded, + Entries: entries, + ErrorMessage: null, + WorkerId: workerId + ); } catch (Exception ex) { // Save exception message to result - throw new NotImplementedException("TODO: T2.2"); + result = new AnalysisResult( + FileName: file.Name, + FullName: file.FullName, + State: AnalysisState.Failed, + Entries: Array.Empty(), + ErrorMessage: ex.Message, + WorkerId: workerId + ); } /* * Save parse result. * [!Important] Remember to lock _syncRoot to prevent data race. */ - throw new NotImplementedException("TODO: T2.2"); + lock (_syncRoot) + { + _analysisResults[file.Name] = result; + } } } } diff --git a/src/LogAnalyzer/WorkQueue.cs b/src/LogAnalyzer/WorkQueue.cs index 23055a5..97b9d72 100644 --- a/src/LogAnalyzer/WorkQueue.cs +++ b/src/LogAnalyzer/WorkQueue.cs @@ -20,17 +20,49 @@ public bool IsCompleted public void Enqueue(T item) { - throw new NotImplementedException("TODO: T2.1"); + lock (_items) + { + if (_isCompleted) + { + throw new InvalidOperationException("Cannot enqueue after CompleteAdding has been called."); + } + + _items.Enqueue(item); + // Wake up one (or all) waiting consumer(s). + Monitor.Pulse(_items); + } } public bool TryDequeue([NotNullWhen(true)] out T? item) { - throw new NotImplementedException("TODO: T2.1"); + lock (_items) + { + // Wait while the queue is empty and adding is not yet complete. + // A while loop is required to handle spurious wakeups (MESA model). + while (_items.Count == 0 && !_isCompleted) + { + Monitor.Wait(_items); + } + + if (_items.Count > 0) + { + item = _items.Dequeue()!; + return true; + } + + item = default; + return false; + } } public void CompleteAdding() { - throw new NotImplementedException("TODO: T2.1"); + lock (_items) + { + _isCompleted = true; + // Wake up all consumers so they can notice completion and exit. + Monitor.PulseAll(_items); + } } } } From a4e396ae600dbf0f99c41ab21614b56a451c43d5 Mon Sep 17 00:00:00 2001 From: sililass Date: Thu, 3 Sep 2026 04:13:01 +0800 Subject: [PATCH 3/4] feat(ch03): implement gRPC Agent, RemoteCli, and type converters --- docs/03-async-grpc/report.md | 133 ++++++++++++++ .../Applications/AgentSession.cs | 155 +++++++++++++++- src/LogAnalyzerAgent/Services/AgentService.cs | 17 +- src/LogAnalyzerRpc/GrpcLogEntryVisitor.cs | 30 +++- src/LogAnalyzerRpc/GrpcTypeConverter.cs | 52 +++++- src/RemoteCli/Program.cs | 170 +++++++++++++++++- 6 files changed, 533 insertions(+), 24 deletions(-) create mode 100644 docs/03-async-grpc/report.md diff --git a/docs/03-async-grpc/report.md b/docs/03-async-grpc/report.md new file mode 100644 index 0000000..9243b29 --- /dev/null +++ b/docs/03-async-grpc/report.md @@ -0,0 +1,133 @@ +# 03-async-grpc 实验报告 + +## 实现的功能介绍 + +本节完成了 Agent(gRPC 服务端)与 RemoteCli(gRPC 客户端)两部分。 + +### T3.1 gRPC Agent(`src/LogAnalyzerRpc`、`src/LogAnalyzerAgent`) + ++ `GrpcLogEntryVisitor`:实现访问者模式,将 `RequestLogEntry`、`InternalLogEntry` 分别转换为 Protobuf 的 `RequestLogEntryMessage` / `InternalLogEntryMessage`,填入 `LogEntryMessage` 的 oneof 字段。 ++ `GrpcTypeConverter`:补全 `LogSeverity` ↔ `LogSeverityEnum`、`LogEventType` ↔ `LogEventTypeEnum` 的双向转换,以及 `ConvertFromGrpc(LogEntryMessage)` 对 Request / Internal 两种消息的反转换(重新构造 `RequestLogEntry` / `InternalLogEntry`,其中 Timestamp 用 `ToDateTimeOffset()` 还原)。 ++ `AgentSession`:实现四个处理逻辑: + - `ChangeDirectory`:空路径返回 `INVALID_ARGUMENT`;目录不存在返回 `DIRECTORY_NOT_FOUND`;成功后返回当前目录与全部 `.log` 文件名。 + - `AnalyzeAll` / `AnalyzeFiles`:未设置目录返回 `INVALID_OPERATION`;`AnalyzeFiles` 对空文件列表返回 `INVALID_ARGUMENT`,对包含不存在文件名的输入(`ArgumentException`)返回 `FILE_NOT_FOUND`,对其他非法操作(`InvalidOperationException`)返回 `INVALID_OPERATION`,其余异常统一转为 `INTERNAL_ERROR`,保证 Agent 永不因用户非法输入而崩溃。 + - `GetAnalysisResult`:文件不存在返回单条 `FILE_NOT_FOUND` 响应;否则返回「header(`AnalysisResultHeaderMessage`,含状态 / 错误信息 / worker id)+ 每条日志一个 `LogEntryMessage`」的响应列表。 ++ `AgentService`:作为 gRPC 服务入口,将每个 RPC 转交给 `AgentSession`;`GetAnalysisResult` 使用服务端流式返回,逐个 `WriteAsync`。 + +### T3.2 RemoteCli(`src/RemoteCli/Program.cs`) + +参照上一节 `LocalCli` 改造为全异步 gRPC 调用(全部使用 `...Async` 方法): + ++ `ShowLogFiles`:调用 `GetLogFilesAsync` 列出文件。 ++ `ReadDegreeOfParallelism`:读取并发度(`0` 表示自动),非法输入循环重试。 ++ `ReadFileNames`:读取逗号分隔的文件名列表,非法输入循环重试。 ++ `AnalyzeFiles` / `AnalyzeAll`:先读并发度(及文件名),再异步调用分析接口,捕获 RPC 异常与业务错误并提示。 ++ `GetAnalysisResult`:用 `client.GetAnalysisResult(request)` 发起服务端流式调用,通过 `ResponseStream.ReadAllAsync()` 逐条读取;打印 header 后用 `KeyValueVisitor.Dump` 输出每条日志的键值对。 + +## 功能记录(终端输出) + +以下为 `src/` 目录下先启动 `LogAnalyzerAgent`(监听 `http://localhost:7777`),再运行 `RemoteCli` 的完整功能流程记录(命令行环境无法截取位图,故以终端文本记录代替截图): + +``` +Connecting to agent at http://localhost:7777... + +Please choose: +1. Show log files. +2. Analyze specified log files. +3. Analyze all log files. +4. Get log file analysis result. +5. Change directory. +6. Exit. +>>> 5 <- 功能5:设置日志目录 +Please input directory containing log files: +>>> dataset + +Please choose: ... +>>> 1 <- 功能1:列出日志文件 +Log files in the current directory: +- basic-fail.log +- basic-multiple.log +- basic.log + +Please choose: ... +>>> 3 <- 功能3:分析全部文件 +Please input degree of parallelism (0 for auto): +>>> 0 +Analysis completed. + +Please choose: ... +>>> 4 <- 功能4:查询分析结果 +Please input a file name to query: +>>> basic.log +Analysis result for 'basic.log': + State: Succeeded, WorkerId: 2 + Entries: + LineNo=0, Timestamp=2026-06-05T16:00:29.0450000+00:00, PodName=userservice-0, Severity=Info, EventType=Call, RequestId=3a013a08-6853-49fc-8f06-50daeb5c1e51, TargetService=authservice, DurationMs=18 + LineNo=1, Timestamp=2026-06-05T16:00:31.0860000+00:00, PodName=userservice-1, Severity=Info, EventType=Request, RequestId=1177c344-115e-4f85-b8ec-c9164d132b79, Method=GET, Path=/api/user/john, StatusCode=404 + LineNo=2, Timestamp=2026-06-05T16:05:45.3220000+00:00, PodName=gateway-0, Severity=Error, EventType=Internal, ExceptionName=System.InvalidOperationException, ExceptionMessage=Failed to load gateway routing configuration. +``` + +## 鲁棒性测试记录(终端输出) + +``` +Please choose: ... +>>> 4 +Please input a file name to query: +>>> nonexist.log <- 查询不存在的文件 +Error: FileNotFound: File 'nonexist.log' is not found. + +Please choose: ... +>>> 2 +Please input degree of parallelism (0 for auto): +>>> 0 +Please input file names to analyze (separated by commas): +>>> basic.log, no-such-file.log <- 分析列表中含不存在的文件 +Analysis failed: FileNotFound: File 'no-such-file.log' is not in the current directory or does not exist. + +Please choose: ... +>>> 5 +Please input directory containing log files: +>>> nonexist-dir <- 切换到不存在的目录 +Error: DirectoryNotFound: Directory 'nonexist-dir' does not exist., please try again: +Please input directory containing log files: +>>> dataset <- 重新输入有效目录,恢复正常 +``` + +可以看到,查询不存在的文件、分析包含不存在文件名的列表、切换到不存在的目录等非法输入,都会被 Agent 以业务错误码(`FILE_NOT_FOUND` / `DIRECTORY_NOT_FOUND`)返回并由 RemoteCli 友好提示,程序与 Agent 都不会崩溃。 + + +## (Q3.1) + +我认为开发网络应用程序与开发非网络应用程序的主要区别有以下几点: + +1. **程序结构从「单机函数调用」变为「分布式服务」**:非网络程序在同一个进程内直接调用函数、共享内存;网络程序则需要把逻辑拆分为服务端与客户端,通过 RPC 在网络上交换数据。因此业务逻辑被拆成「服务端如何提供能力」和「客户端如何请求能力」两个面,开发时必须在两端同时考虑。 +2. **数据不再是本地的对象,而是需要「序列化 / 反序列化」**:本地程序直接引用对象即可;网络程序传输的是字节流(本作业中是 Protobuf 消息),因此需要额外编写类型转换层(`GrpcTypeConverter`、`GrpcLogEntryVisitor`),并维护 C# 类型与 Protobuf 类型、以及两边命名法(PascalCase ↔ snake_case)的一致性。 +3. **必须考虑网络的不确定性**:网络请求可能超时、被中断、服务端不可达;返回的错误除了「业务错误」(如文件不存在)还有「传输层错误」(RpcException)。因此客户端需要做异常捕获,服务端需要保证对任何输入都不会崩溃(这直接影响服务的可用性)。 +4. **并发与状态管理更复杂**:Agent 作为常驻服务是「有状态」的(保存当前目录、分析结果),且可能同时被多个客户端调用,必须通过依赖注入将 `LogFileAnalyzer` / `AgentSession` / `AgentService` 注册为单例,并注意内部共享状态的线程安全;非网络程序则没有「并发请求」这个维度。 +5. **调试与部署方式不同**:需要同时启动服务端与客户端两个进程来联调,难以像单机程序那样一步打断点;启动参数(监听地址、端口)、环境变量(`ASPNETCORE_URLS`)等也引入了额外复杂度。 + +额外的难点与复杂之处总结为:类型系统的跨语言/跨进程映射、错误处理的多层化(传输错误 + 业务错误)、有状态服务的并发安全、以及「服务端永不因非法输入崩溃」这一硬性要求。 + +## (Q3.2.b) + +本次作业使用了 AI(Cline 编码助手)。 + +### 我给予 AI 的提示词 + +> 阅读 `docs/03-async-grpc` 的说明文档、`src/test-03-async-grpc` 测试代码以及 `Protos/log_analyzer.proto`,补齐 `GrpcTypeConverter`、`GrpcLogEntryVisitor`、`AgentSession`、`AgentService` 与 `RemoteCli/Program.cs` 中的 TODO 实现,使 `dotnet test test-03-async-grpc -c Release` 全部通过;随后启动 Agent 并运行 RemoteCli 做端到端验证,最后撰写 `docs/03-async-grpc/report.md`。 + +### 对 AI 的使用方式 + +主要是让 AI 编写一部分作业代码(T3.1 / T3.2 的 TODO 实现),同时也向 AI 询问了一些 gRPC 客户端流式调用的具体写法(例如如何用 `ResponseStream.ReadAllAsync()` 读取服务端流),以及 Protobuf oneof 字段的 C# 使用方式。 + +### AI 的解答是否出现过错误 + +在初版实现中没有出现功能性错误,测试一次通过;但 AI 在端到端联调时一度没有注意到 `RemoteCli` 与 `LocalCli` 的不同——`RemoteCli` 启动后**不会自动提示输入目录**,需要先通过菜单选项 5 切换目录,导致最初的联调输入序列里目录设置没有生效。经分析 `Program.cs` 框架后修正了联调流程。 + +### 从 AI 那里得知的新知识 + ++ gRPC 服务端流式返回在 C# 中通过在 `IServerStreamWriter` 上反复 `WriteAsync` 实现,客户端则通过 `call.ResponseStream.ReadAllAsync()` 逐条读取。 ++ Protobuf 的 `oneof` 字段在 C# 中表现为 `PayloadCase` 枚举(如 `GetAnalysisResultResponse.PayloadOneofCase.Header`)与对应的 `Header` / `LogEntry` 属性,只能设置其中一个。 ++ `optional string` 字段会生成 `HasErrorMessage` 属性用于判断字段是否被显式设置。 ++ 有状态 gRPC 服务需要把相关类注册为单例(`AddSingleton`),否则每次请求都会新建状态导致相互覆盖。 + diff --git a/src/LogAnalyzerAgent/Applications/AgentSession.cs b/src/LogAnalyzerAgent/Applications/AgentSession.cs index 2531f22..5836485 100644 --- a/src/LogAnalyzerAgent/Applications/AgentSession.cs +++ b/src/LogAnalyzerAgent/Applications/AgentSession.cs @@ -3,6 +3,7 @@ using LogAnalyzer; using LogAnalyzerRpc.Protos; using LogAnalyzerRpc; +using LogParser.Models; using LogParser.Visitors; namespace LogAnalyzerAgent.Applications @@ -79,22 +80,168 @@ public Task GetLogFiles(Empty empty, CancellationToken canc public Task ChangeDirectory(ChangeDirectoryRequest request, CancellationToken cancellationToken) { - throw new NotImplementedException("TODO: T3.1"); + var response = new ChangeDirectoryResponse(); + try + { + if (string.IsNullOrEmpty(request.DirectoryPath)) + { + response.Status = CreateErrorOperationStatus( + AgentErrorCode.InvalidArgument, + "Directory path cannot be empty."); + return Task.FromResult(response); + } + + if (!_analyzer.ChangeDirectory(request.DirectoryPath)) + { + response.Status = CreateErrorOperationStatus( + AgentErrorCode.DirectoryNotFound, + $"Directory '{request.DirectoryPath}' does not exist."); + return Task.FromResult(response); + } + + response.CurrentDirectory = _analyzer.CurrentDirectory ?? ""; + response.FileNames.AddRange(_analyzer.GetLogFiles()); + response.Status = CreateNoErrorOperationStatus(); + } + catch (Exception ex) + { + response.Status = CreateInternalErrorOperationStatus(ex); + _logger.LogError(ex, "An error occurred while changing directory."); + } + return Task.FromResult(response); } public Task AnalyzeAll(AnalyzeAllRequest request, CancellationToken cancellationToken) { - throw new NotImplementedException("TODO: T3.1"); + var response = new AnalyzeAllResponse(); + try + { + if (!_analyzer.HasDirectory) + { + response.Status = CreateErrorOperationStatus( + AgentErrorCode.InvalidOperation, + "No directory has been set yet."); + return Task.FromResult(response); + } + + _analyzer.AnalyzeAll(request.DegreeOfParallelism); + response.Status = CreateNoErrorOperationStatus(); + } + catch (Exception ex) + { + response.Status = CreateInternalErrorOperationStatus(ex); + _logger.LogError(ex, "An error occurred while analyzing all log files."); + } + return Task.FromResult(response); } public Task AnalyzeFiles(AnalyzeFilesRequest request, CancellationToken cancellationToken) { - throw new NotImplementedException("TODO: T3.1"); + var response = new AnalyzeFilesResponse(); + try + { + if (!_analyzer.HasDirectory) + { + response.Status = CreateErrorOperationStatus( + AgentErrorCode.InvalidOperation, + "No directory has been set yet."); + return Task.FromResult(response); + } + + if (request.FileNames.Count == 0) + { + response.Status = CreateErrorOperationStatus( + AgentErrorCode.InvalidArgument, + "No file names are specified."); + return Task.FromResult(response); + } + + _analyzer.AnalyzeFiles(request.DegreeOfParallelism, request.FileNames); + response.Status = CreateNoErrorOperationStatus(); + } + catch (ArgumentException ex) + { + response.Status = CreateErrorOperationStatus(AgentErrorCode.FileNotFound, ex.Message); + _logger.LogError(ex, "An error occurred while analyzing specified log files."); + } + catch (InvalidOperationException ex) + { + response.Status = CreateErrorOperationStatus(AgentErrorCode.InvalidOperation, ex.Message); + _logger.LogError(ex, "An error occurred while analyzing specified log files."); + } + catch (Exception ex) + { + response.Status = CreateInternalErrorOperationStatus(ex); + _logger.LogError(ex, "An error occurred while analyzing specified log files."); + } + return Task.FromResult(response); } public IReadOnlyList GetAnalysisResult(GetAnalysisResultRequest request, CancellationToken cancellationToken) { - throw new NotImplementedException("TODO: T3.1"); + var responses = new List(); + try + { + if (!_analyzer.TryGetAnalysisResult(request.FileName, out var result) || result is null) + { + responses.Add(new GetAnalysisResultResponse() + { + Status = CreateErrorOperationStatus( + AgentErrorCode.FileNotFound, + $"File '{request.FileName}' is not found.") + }); + return responses; + } + + var header = new AnalysisResultHeaderMessage() + { + FileName = result.FileName, + FullName = result.FullName, + State = GrpcTypeConverter.ConvertToGrpc(result.State), + WorkerId = result.WorkerId, + }; + if (result.ErrorMessage is not null) + { + header.ErrorMessage = result.ErrorMessage; + } + + responses.Add(new GetAnalysisResultResponse() + { + Header = header, + Status = CreateNoErrorOperationStatus() + }); + + foreach (var entry in result.Entries) + { + responses.Add(new GetAnalysisResultResponse() + { + LogEntry = GrpcTypeConverter.ConvertToGrpc(entry), + Status = CreateNoErrorOperationStatus() + }); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occurred while retrieving analysis result."); + return new List() + { + new GetAnalysisResultResponse() + { + Status = CreateInternalErrorOperationStatus(ex) + } + }; + } + return responses; + } + + private static OperationStatusMessage CreateErrorOperationStatus(AgentErrorCode code, string message) + { + return new OperationStatusMessage() + { + Success = false, + Code = code, + Message = message, + }; } } } diff --git a/src/LogAnalyzerAgent/Services/AgentService.cs b/src/LogAnalyzerAgent/Services/AgentService.cs index 591dcad..1bc03eb 100644 --- a/src/LogAnalyzerAgent/Services/AgentService.cs +++ b/src/LogAnalyzerAgent/Services/AgentService.cs @@ -29,27 +29,32 @@ public override Task GetAgentStatus(Empty empty, ServerC public override Task ChangeDirectory(ChangeDirectoryRequest request, ServerCallContext context) { - throw new NotImplementedException("TODO: T3.1"); + return _session.ChangeDirectory(request, context.CancellationToken); } public override Task GetLogFiles(Empty empty, ServerCallContext context) { - throw new NotImplementedException("TODO: T3.1"); + return _session.GetLogFiles(empty, context.CancellationToken); } public override Task AnalyzeAll(AnalyzeAllRequest request, ServerCallContext context) { - throw new NotImplementedException("TODO: T3.1"); + return _session.AnalyzeAll(request, context.CancellationToken); } public override Task AnalyzeFiles(AnalyzeFilesRequest request, ServerCallContext context) { - throw new NotImplementedException("TODO: T3.1"); + return _session.AnalyzeFiles(request, context.CancellationToken); } public override async Task GetAnalysisResult(GetAnalysisResultRequest request, IServerStreamWriter responseStream, ServerCallContext context) { - throw new NotImplementedException("TODO: T3.1"); + var responses = _session.GetAnalysisResult(request, context.CancellationToken); + foreach (var response in responses) + { + await responseStream.WriteAsync(response); + } + } + } - } } diff --git a/src/LogAnalyzerRpc/GrpcLogEntryVisitor.cs b/src/LogAnalyzerRpc/GrpcLogEntryVisitor.cs index eb69232..3196aac 100644 --- a/src/LogAnalyzerRpc/GrpcLogEntryVisitor.cs +++ b/src/LogAnalyzerRpc/GrpcLogEntryVisitor.cs @@ -30,12 +30,38 @@ public LogEntryMessage Visit(CallLogEntry entry) public LogEntryMessage Visit(RequestLogEntry entry) { - throw new NotImplementedException("TODO: T3.1"); + return new LogEntryMessage() + { + RequestLogEntry = new RequestLogEntryMessage + { + LineNo = entry.LineNo, + Timestamp = Timestamp.FromDateTimeOffset(entry.Timestamp), + PodName = entry.PodName, + Severity = GrpcTypeConverter.ConvertToGrpc(entry.Severity), + EventType = GrpcTypeConverter.ConvertToGrpc(entry.EventType), + RequestId = entry.RequestId, + Method = entry.Method, + Path = entry.Path, + StatusCode = entry.StatusCode, + } + }; } public LogEntryMessage Visit(InternalLogEntry entry) { - throw new NotImplementedException("TODO: T3.1"); + return new LogEntryMessage() + { + InternalLogEntry = new InternalLogEntryMessage + { + LineNo = entry.LineNo, + Timestamp = Timestamp.FromDateTimeOffset(entry.Timestamp), + PodName = entry.PodName, + Severity = GrpcTypeConverter.ConvertToGrpc(entry.Severity), + EventType = GrpcTypeConverter.ConvertToGrpc(entry.EventType), + ExceptionName = entry.ExceptionName, + ExceptionMessage = entry.ExceptionMessage, + } + }; } } } diff --git a/src/LogAnalyzerRpc/GrpcTypeConverter.cs b/src/LogAnalyzerRpc/GrpcTypeConverter.cs index 029122e..aa31134 100644 --- a/src/LogAnalyzerRpc/GrpcTypeConverter.cs +++ b/src/LogAnalyzerRpc/GrpcTypeConverter.cs @@ -20,12 +20,24 @@ public static AnalysisStateEnum ConvertToGrpc(AnalysisState state) public static LogSeverityEnum ConvertToGrpc(LogSeverity severity) { - throw new NotImplementedException("TODO: T3.1"); + return severity switch + { + LogSeverity.Info => LogSeverityEnum.Info, + LogSeverity.Warning => LogSeverityEnum.Warning, + LogSeverity.Error => LogSeverityEnum.Error, + _ => throw new ArgumentOutOfRangeException(nameof(severity), severity, null) + }; } public static LogEventTypeEnum ConvertToGrpc(LogEventType eventType) { - throw new NotImplementedException("TODO: T3.1"); + return eventType switch + { + LogEventType.Call => LogEventTypeEnum.Call, + LogEventType.Request => LogEventTypeEnum.Request, + LogEventType.Internal => LogEventTypeEnum.Internal, + _ => throw new ArgumentOutOfRangeException(nameof(eventType), eventType, null) + }; } public static LogEntryMessage ConvertToGrpc(LogEntry entry) @@ -46,12 +58,24 @@ public static AnalysisState ConvertFromGrpc(AnalysisStateEnum state) public static LogSeverity ConvertFromGrpc(LogSeverityEnum severity) { - throw new NotImplementedException("TODO: T3.1"); + return severity switch + { + LogSeverityEnum.Info => LogSeverity.Info, + LogSeverityEnum.Warning => LogSeverity.Warning, + LogSeverityEnum.Error => LogSeverity.Error, + _ => throw new ArgumentOutOfRangeException(nameof(severity), severity, null) + }; } public static LogEventType ConvertFromGrpc(LogEventTypeEnum eventType) { - throw new NotImplementedException("TODO: T3.1"); + return eventType switch + { + LogEventTypeEnum.Call => LogEventType.Call, + LogEventTypeEnum.Request => LogEventType.Request, + LogEventTypeEnum.Internal => LogEventType.Internal, + _ => throw new ArgumentOutOfRangeException(nameof(eventType), eventType, null) + }; } public static LogEntry ConvertFromGrpc(LogEntryMessage entryMessage) @@ -67,8 +91,24 @@ public static LogEntry ConvertFromGrpc(LogEntryMessage entryMessage) TargetService: entryMessage.CallLogEntry.TargetService, DurationMs: entryMessage.CallLogEntry.DurationMs ), - LogEntryMessage.EntryOneofCase.RequestLogEntry => throw new NotImplementedException("TODO: T3.1"), - LogEntryMessage.EntryOneofCase.InternalLogEntry => throw new NotImplementedException("TODO: T3.1"), + LogEntryMessage.EntryOneofCase.RequestLogEntry => new RequestLogEntry( + LineNo: entryMessage.RequestLogEntry.LineNo, + Timestamp: entryMessage.RequestLogEntry.Timestamp.ToDateTimeOffset(), + PodName: entryMessage.RequestLogEntry.PodName, + Severity: ConvertFromGrpc(entryMessage.RequestLogEntry.Severity), + RequestId: entryMessage.RequestLogEntry.RequestId, + Method: entryMessage.RequestLogEntry.Method, + Path: entryMessage.RequestLogEntry.Path, + StatusCode: entryMessage.RequestLogEntry.StatusCode + ), + LogEntryMessage.EntryOneofCase.InternalLogEntry => new InternalLogEntry( + LineNo: entryMessage.InternalLogEntry.LineNo, + Timestamp: entryMessage.InternalLogEntry.Timestamp.ToDateTimeOffset(), + PodName: entryMessage.InternalLogEntry.PodName, + Severity: ConvertFromGrpc(entryMessage.InternalLogEntry.Severity), + ExceptionName: entryMessage.InternalLogEntry.ExceptionName, + ExceptionMessage: entryMessage.InternalLogEntry.ExceptionMessage + ), _ => throw new ArgumentException($"Unknown entry type: {entryMessage.EntryCase}", nameof(entryMessage)) }; } diff --git a/src/RemoteCli/Program.cs b/src/RemoteCli/Program.cs index de0ac99..cdf85ff 100644 --- a/src/RemoteCli/Program.cs +++ b/src/RemoteCli/Program.cs @@ -116,32 +116,190 @@ 6. Exit. private static async Task ShowLogFiles(LogAnalyzerAgentServiceClient client) { - throw new NotImplementedException("TODO: T3.2"); + GetLogFilesResponse response; + try + { + response = await client.GetLogFilesAsync(new Empty()); + } + catch (Exception ex) + { + Console.WriteLine($"Failed to get log files: {ex.Message}"); + return; + } + + if (!response.Status.Success) + { + Console.WriteLine($"Error: {response.Status.Code}: {response.Status.Message}"); + return; + } + + if (response.FileNames.Count == 0) + { + Console.WriteLine("There are no log files in the current directory."); + return; + } + + Console.WriteLine("Log files in the current directory:"); + foreach (var fileName in response.FileNames) + { + Console.WriteLine($"- {fileName}"); + } } private static int ReadDegreeOfParallelism() { - throw new NotImplementedException("TODO: T3.2"); + while (true) + { + Console.WriteLine("Please input degree of parallelism (0 for auto):"); + Console.Write(">>> "); + Console.Out.Flush(); + var input = Console.ReadLine(); + if (int.TryParse(input, out var degree) && degree >= 0) + { + return degree; + } + Console.WriteLine("Invalid input, please try again."); + } } private static List ReadFileNames() { - throw new NotImplementedException("TODO: T3.2"); + while (true) + { + Console.WriteLine("Please input file names to analyze (separated by commas):"); + Console.Write(">>> "); + Console.Out.Flush(); + var input = Console.ReadLine(); + if (string.IsNullOrEmpty(input)) + { + Console.WriteLine("Invalid input, please try again."); + continue; + } + + var fileNames = input.Split(',') + .Select(name => name.Trim()) + .Where(name => !string.IsNullOrEmpty(name)) + .ToList(); + if (fileNames.Count == 0) + { + Console.WriteLine("Invalid input, please try again."); + continue; + } + return fileNames; + } } private static async Task AnalyzeFiles(LogAnalyzerAgentServiceClient client) { - throw new NotImplementedException("TODO: T3.2"); + var degree = ReadDegreeOfParallelism(); + var fileNames = ReadFileNames(); + var request = new AnalyzeFilesRequest() + { + DegreeOfParallelism = degree, + }; + request.FileNames.AddRange(fileNames); + + AnalyzeFilesResponse response; + try + { + response = await client.AnalyzeFilesAsync(request); + } + catch (Exception ex) + { + Console.WriteLine($"Analysis failed: {ex.Message}"); + return; + } + + if (!response.Status.Success) + { + Console.WriteLine($"Analysis failed: {response.Status.Code}: {response.Status.Message}"); + return; + } + Console.WriteLine("Analysis completed."); } private static async Task AnalyzeAll(LogAnalyzerAgentServiceClient client) { - throw new NotImplementedException("TODO: T3.2"); + var degree = ReadDegreeOfParallelism(); + var request = new AnalyzeAllRequest() + { + DegreeOfParallelism = degree, + }; + + AnalyzeAllResponse response; + try + { + response = await client.AnalyzeAllAsync(request); + } + catch (Exception ex) + { + Console.WriteLine($"Analysis failed: {ex.Message}"); + return; + } + + if (!response.Status.Success) + { + Console.WriteLine($"Analysis failed: {response.Status.Code}: {response.Status.Message}"); + return; + } + Console.WriteLine("Analysis completed."); } private static async Task GetAnalysisResult(LogAnalyzerAgentServiceClient client) { - throw new NotImplementedException("TODO: T3.2"); + Console.WriteLine("Please input a file name to query:"); + Console.Write(">>> "); + Console.Out.Flush(); + var fileName = Console.ReadLine(); + if (string.IsNullOrEmpty(fileName)) + { + return; + } + + var request = new GetAnalysisResultRequest() + { + FileName = fileName, + }; + + try + { + using var call = client.GetAnalysisResult(request); + var visitor = new KeyValueVisitor(); + await foreach (var response in call.ResponseStream.ReadAllAsync()) + { + if (!response.Status.Success) + { + Console.WriteLine($"Error: {response.Status.Code}: {response.Status.Message}"); + return; + } + + switch (response.PayloadCase) + { + case GetAnalysisResultResponse.PayloadOneofCase.Header: + var header = response.Header; + Console.WriteLine($"Analysis result for '{header.FileName}':"); + Console.WriteLine($" State: {header.State}, WorkerId: {header.WorkerId}"); + if (header.HasErrorMessage) + { + Console.WriteLine($" ErrorMessage: {header.ErrorMessage}"); + } + if (header.State == AnalysisStateEnum.Succeeded) + { + Console.WriteLine(" Entries:"); + } + break; + case GetAnalysisResultResponse.PayloadOneofCase.LogEntry: + var entry = GrpcTypeConverter.ConvertFromGrpc(response.LogEntry); + var kvResult = visitor.Dump(entry); + Console.WriteLine($" {string.Join(", ", kvResult.Select(pair => $"{pair.Key}={pair.Value}"))}"); + break; + } + } + } + catch (Exception ex) + { + Console.WriteLine($"Failed to get analysis result: {ex.Message}"); + } } } } From fd3a56d522d52bc155a3fa47701c705c51b640c4 Mon Sep 17 00:00:00 2001 From: sililass Date: Thu, 3 Sep 2026 04:13:14 +0800 Subject: [PATCH 4/4] feat(ch04): implement Avalonia GUI client with list-based result display --- docs/04-avalonia/assets/gui-main.png | Bin 0 -> 178822 bytes docs/04-avalonia/report.md | 178 ++++++++++++++++++ .../LogAnalyzerClient/Models/RemoteModels.cs | 14 +- .../ViewModels/MainViewModel.cs | 164 +++++++++++++++- .../LogAnalyzerClient/Views/MainView.axaml | 5 +- 5 files changed, 348 insertions(+), 13 deletions(-) create mode 100644 docs/04-avalonia/assets/gui-main.png create mode 100644 docs/04-avalonia/report.md diff --git a/docs/04-avalonia/assets/gui-main.png b/docs/04-avalonia/assets/gui-main.png new file mode 100644 index 0000000000000000000000000000000000000000..4ec9bff4b2fa107b753592df2d61ccbaf9ad4e8a GIT binary patch literal 178822 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 z%0A#zo?`^>uEjZ_C0+_H3XlTo+kbNFKo6bysmJ~rKy34SwDZ0s_8p0SgnmYpsSqQ6 z1oad>gyW$^*33824OkuM!x};J=?vjPiKFeSL*>O*j`my??)z ztcU(UB@kbRaAM0=pv#rysy^4XGsT>s2BVfr!Ge8Y4Q~Csj%=l8o|Cg zGO+TSN~XUyv(@Dp9*~1M9;!^23Ta*S(GJ7l#X5%er1_Mceh5%|Dj@1vSm6i{j~tfY z>=U8g3#mj@Q<4VbeNQ}8N2>8c{Kho zT>)m5$wVv;d*2lCG*0Ye+)aRadov_N=jRKCf3H+H3VE#dAoXxztO!k=F$A|;C{jxf zJO%=rAygJU%FPmhDX_c^~w~;J?~FBfAEdA-N}{G?4-lV z1O&>sW_qo`w=6OyNn2|6?u{kFI4nC`h@3J=c^c-2z%FaodWcB2xT>jCe^B|)di^w3b15EJJyT0p3n-iVn@now%VhKcF zQbR+H1|TzZa!?0c8QKffr(WuH93P4$Ido2ann)z{x3qTgZQjE_wpZ5lo^^BmtM#gl z*;p^1Gz~;2UmeU?Z;$mt)amX$Oe!ct#f$ReJ@>XT5;S$fv9LTu8UUQ*_z94iJclZv;hhp0?SUBi6!6o`I91wuaauu+yq9YnN zVWw04a1f6w1mg$O`nlEONv7&xsezLsg(I9^y<`Sglfy8+g$)x^VEr;-`mui~{v=`h1+p$v@!I{sfW%s;K8A`yeqQD6On- z6DRN7brYN8@GI#Eqyb9E0V4eNUNgeDQ_O-o-QIJs{f5CR_54`m+p^vNcsvlo$Iui; zqAj`3v}Et#$f(znqtJG*aR~p{|JNPL!k_!csO;)WT_36$y1iCuhcX}5%CNZDwu7Rm ziYJr8g3a8d4>^)}EFaCYq=x2%V9(>zv499DCk$y(Lm-*^3g z?TskmWBnKqo@&xCOjG+RPILTbP5&EkfKb!lM24;coq+Ng#cRr}QEu`?GF0&lPyy#c z13=j0^oQPiBoz*2tTH=hhOIqb#dESD3xqSp7R@9`Ji~v=KsuO@7}Hrxe8l7vIFbw@E00eP&OnFJ24Au9QM zdw}ysPBC^XwhWeGPboK+0$agJl;jkYHG=er2m4j9$E6|13f1X#a<9~7f900mKM)!XbF>X$FVP!*$!P^ zetP3QvDNr9ifO}eQnR*^j`vXr=6L$EO?x6EbbA+ekfC17*tV21RZA&tmrvZ`VsOxPlqrzVo z_+K?Sr|FA>wSt(IytG#Fd$<~GJ+M2-NXEk^?POM)KZ1C)E)`U%!MmMQOb5<`JzRrA z>K26N>kfe`xE(r!S9zs7Zy_9t{t?jSm?6&_NM0pluFRsIrD0(a%IBI+{DAfPpp|5CLfzHa|-jbbYm;JbVCt{Po_?){XJ?B)7X*moEwHqO-3m?pxGtH2 zt`NkMGF=I}K<0ZJo{8(ReEM9;H6>g;C3$6?bT0`I1-8P1zP=i@4MNb6H0{cNWR)N@ z8%~d}j}VOfxO(N2 zbVkQL=#8MqTd^{dfN3V`jRE-MDF-AvsFgw?y=u4v8Ccs}@jMj}&xHU$VaV+nufS!o zkir!uanaS;h0zVx>m!EsX=yb{P3%NE3PfDE8I}j=`$@F7f`u`Ikeudhf3E-^m~4It z8s8US(Hx#MyVwmW5C#sR0GKFZpaY>rZ*Wz0D9-@W9MyoLv>db}?THv6eEMSqJh%## zpzx!;xQ?;n*UdehS+cwl_`35zJy`!h`RNWQGie-7qGWUC^mS;?HegTf-pWhqhY-V4 z-gj7~xvb`onN}--WDw1@L2X?T52^t;P~d}P_QMN~*M!Nf?|z#cLWP_?+EUW#fQSNq$` z*Y%w|4&s2-m|t5Zc2gYy?P)eR9S((ug%AB9V)NzKVId;e&v<&u^q{|}gDLV41hjnL zyeTB!2IUEfBQ#o^v0Unj9bSiZkVM;l6Jdk@fCArtM{wO8B-0K*#Ee9>q9f^<>ZhxBgM&vY-kHw>hXG6i>m*#=!iGhA3fm^M+VXU{?J1k#Wv2RLyf|iq#>7=GkvGt8DEMZC%WjXN9olcYAI7ZIQX+juX_C8*yIuj-BWH{yEMxaE4 zx)pTD5si8U%9c7&eN4O2;D@z@nF%4`R+PH#0{N!+5~K3D8dKXh)#@q+OFQQ0xMlgb z$xhsjUHZagwiw`!j)6c?pvKUF2i5)Ff-;8^mK!XRbBYj~qG^taqGs89y?D*qb8L`1 zj&1KKkXAqEMY|JQ-g}2tj36lKgwzB_^iJfcyDPIq>rU}yYEU{0uY`gLJ0lfbpmEP& z8f1B^$O+mc-r+Qfi-%7j81(LX?L+A>3Nl44mtR!dmw=9_TYJ;P-a>7sZJ*CJ20&s( zwv1}bLJ|F5+PIO}3a6!%EzZ2*Zt}Lx1btnB+3wkU?tKM;^|}4LFoWCp*_pFrGrtrK z$J>E27egUVLCBX(hP1zn^q$2 zlLxTz6%ScU6a+;t%;=Ym6bZ(*=U9%s{`|y_uoWcX0dH-|u|CPf2v4`+|c zWR9L$E!Lr?;z@KdpQJ@etl0dHj$rLHix%tGg_ZtL3+}InzS?Ny-;`%*@U!|y_vlZ$qyrz0>n>!ZGE;P-YPosZMf&>f|YWW8E7G`V5 z{5(J+|Eot6DM%v?+(bdDcaJK8`#56PBTrm``5-My2lDXyH4Lu9N`O&PKhe_7h4Dd@ zfRCD9UsH(;?`)S_0)sLL*@s1~L)7Dn^v33<-a;TiRT`XFS_l@OX@O@GBc%35fSAdg zAv}NhMBQh-Wdnxx1HmqnOf)4W`3{jD3FJ&h;(@r--6tQ zpL(?}jN}NxZGVykWhU+hD8o3(ZH9YpBI&Vt$_m*Js@3a8Q^&*Zw+Jzd+LUf#-TNC- zH!~`kl$lsdk7kN1hoH(x1LL*e2Y2NNrW~uGT*is3v9$cm$)#t<$BQU&E!eahoDXaRzU|N{P{?6vv}=d8YU`g-cKx!G?{Bv35QBQX(~# zT-`Uvzajwt#qo&WpOZ8n{4$taJX^l0KQy6O1!hOniNO#K=nvcTNasA~!s5LlSp7Ey z3hbAIX;@!|t|GD=mul?pEyzn$BqEaQrheletYr$$gnB0`FDYmKuKwIa??jEM(W@sk zZ^42Kv$9h+9cjHzH8hc7hr6i71TvZgw#jq}B$SMw@*q^Gce^}tU*5cJh=6F|Fi_Ki zkpnnsFz)aD$9?f$pl{0;zo%lUm=Btis7{5%UkP zHMVGS`u!pl%4j-fec{4uZW+$SnFC=% z!sbJSeAvRkG0p-20xI)$xUm-e1`SvGbJFu)88(2LkKME?;FWM5*hw+ZeEfeqLkfCq zyiR`ORu|2DPwQU(j zf^Tde{Yd7R5aN(wg80?|(>$?j^;-Sd$dK*CCFwLC{Cj9uj@2kIogA`8T&mGwI7;2y zasxOnC_hl~d@Z|HzhjIer8C!oY~5J<`+<#@-@SVxWIp1|MuzcP!Ci3Om%6VdLCTxq zqJn~gtM!l18E9cP3*hjmJu>pPnxea7qB zDF#{F))yMT;sV=DDCJG|DKzX4rb4MW+GO6@dkmC#OPjU?Av!v_S9-Z1Az^iR&KjWC z*IxaBXeEhlp3Zh^?)O^V;foX&*z-`YTM6$BEeh|LM7xPf&oT-5%BZsqdK;lXYJ$=T zB92|o(yy#)vW<7-k!-+Y!z^J)%rN8!(Iz3ssxB-Y#Kv454_$;&j@y3U_co=kFXb3U zfQ^(D>9To`*dZ|N+R(Dl^wxgmC>hffNb%r@`e17{sh<~L^Kj@D@ynmTFNj1R`y(g- z(MmZNuAma}aYc0724}66hQ=2=6MdE$~fV`|n}(9Cn9&TvVjT)S>-vJu-Ua<*|)R_Enr#)Z0nL3AZZYb(vr2ZPB9H09DI(Exf2Ep5*U0x({AN~phO9r|2|nm_)17CUQknk zG3oxc4QVE=e=3DeiM0=}j@F2mk@uyb=^x-pyS0V}DIo;Fd`UFr<)(d7G7=D1$f@k1 z6)M9Wrdz<9W7QU0si}*9+9*PZfiENJ;rb0p*@dSWSf$c7gt3tzlEja~$5xKJ zhpit-CxnZi?wNtC0R z|Gu!JRNIleW`owR1~0S7Ub&BQjEYAb&Pq+X-r5hd+jtmBkoZ^wAxsiy{8^=SPh9Fi ziI^OD+yh z7$97O{sxJVgSk!!zXxGB%ID8$RucgbP=QPCz+ohAFswLzx*UgMV`l=HFVJCL<@VQ4 zPtZJ{!*p`&jk`Itly|upcpL@#&3(E>7?qv^Ui^(F9by$2?tcSJ@Afw_JyaaD%J3LW zRT8KbE9*wVayIRf#?UVM@egWi7iC+!)2pjnc(HU-k?&z5lL9e3Wmz_ufWijw9I3u+ z`VXCNnQdV?ytFVI$*=ba*#3Gm=rSZ^Zh$uer%+AuS~UP(c>YbT1wf!*=?sAdTMAZ= z9fgOmZiVM)w0#iT)d68cHxVoIu;Oi2j^Xrb=9FT~$Lqjnag9<_*D^@%G|*&6ztN{j zN}9gwS%qz40cmfSJs@g)A9@0%jb>(OO54fKZ^N*tn_Vsa5I*^K_ijihwSJXD8hFPZ zKKhC8VF z;9>-AX>r;bEg5Q{0y->ManOD5K{B0S1n4k zv{U@?6QaZClYtJ+q>7%X`}0=l)B4fQJVijVU$wfBJIEi!@z6kDaN<%h3R5sq~fKCOS#o3W7qL-4c;n3@^3DOK{M@pYfPojlEqfwzo zX2s_-tLnOeAKWc$6?bkD9JIVVetQgONFDGIrd6be)`;;II_>UnUuXg5I>;x*6TV=zUfV@0ijfnyEskF6zz`~Uy(f2IZfzk3?D%UQ`Id#TLA=c2v@AbZ)xZ{R(0M<0>BL8#LEMVL)X2geM-H6cZWXNGU~Qs8%Cns;OR9UMtne(qu*1 zq7wu%c2m1O2eT&HV1g(NX5)u&AhmhOrV27DoyujE03#04^JUI_dpI=+l?A86X&-Vds!gJE;HT9pU$T-~@lyh{ct3hf2 zQ|p$yCxhKi9#vikL0&{D@pf$Ss41Ea8UEpeNs~_ay@z)|(23411$gc>nDC5^;(_Lq zTLb1>eSf=~^ZgJt#pwPEkS|w$Wj@AsFfIXX*T7b*)T8c;J%R=pAPJSm0@&J!0{_lb zNYq55N+2e_@8swyol^U#d}}j^xe?Ry&4C0!H1q->PN&n5jn)3+*Dlq52>*8F@rH>? zK~GJY*&;i*2#da#)#Zn(!H;+Ug9(~;2D3U6pZV6KNc}-b;jS%~Qm#4)_~y%C8kpdX zY2%>#B91O>eOa&goCE>fsL(JvI_4I#aSeVSz5-#f%3ceK>o-?h&*J!aJxKm3%}BDj zR%4W~HsmuY_T?o=^CrwUyj=}`^hfH7r}sHr?gZVTgH95$Ui&Q2r!B9$_{h2VVIBOl zGXapcx6@MGRTYSm+QQ#=No4N2gG@3Bgh1j+hhCBT%t*#zJo69Vlsb%#`p2vFMPfZ8 zT1iGk;O9T4zp=kZN_))M{?c4-c|PN`|;jlyC=fx!=cAbcm3AP50cGBPtYta6z3M<&0{8+3Qjo^`^#89>M$X z6~`)e-2bqeYWagla*#3bfG{L95kI|CjS{yr?^-n=fvqENwwpIq-Kzz@IY^3AMibZb z$awRN0s$=u*d$+XgM zq@zTeG~0d_nO^DXE)yX-#I0vtS;u%Z+*InYl~pI^x4kDHAJII$e!b@D$7fvVZkz0u zx>jb%J*uGAs339ic)_cv&drl_$|=6b^ED-3iK!hQRee*xo3VL_+gYMnE@-wxWq{M| zl6TZf<)x}@;tr=Or>v0`O1`T;rSnah`c8wlxg~j8^qX&2Y2(A87alEfETW!2fzPNB z41y`!JXvY1Mmot}+fKB#f{MYQrTgU$=PNr!3@0GUikQ%=dDAXF@dIm5_iEhJCw9Dx zi5B66m>L(1HLR;6kx0`MO_H+VJq%lGql7P&P`SZE?HzQ@OjsJv-z*r!0pERlxkpHN z=)S`8k^W1?8DH$faou0$ZA1_c`CjKFmOSp@y({~-;#|v$k`>}(BAHz%E!*+%=8IgK zOD?XA|p09TZ>yJAB5T_=Ik!ar+u;-X{4ez9q3 zrnKXihuZDws00ec*lE}7^Y9R`8Ny#RWw**DB=Z(9OwA}8t?Ur*Y8Uj4mFehl(}31+ z!<%&rO%z?wyJw9Lh51xfaa-Vt4Gt@w7e`028ril|C@`l{aQsA_56ypU_OdznD(;?} z5#N79Y>*k#^m%k?H9!z%5^m@L@vQ;zn~H zl}K9dd)!oJP>Vj?h%CZ;yvfD1%>98#B^j?Y3Da!|xWjfWEg>dUP)H{y6MZ-9&r~Y4 zI@iLZ4f6<4WN&iu@w0Px^Frg$9{UpdZ>bq~ygMG1LvUDmBJVQQzY^;kSZ=>x%Du1L zb9{M2k=uB=uc^qpBnXqq}*TK$V{)oczAn#{;)V^v2Clz!SOg*L5t?N z&42Db90Owu9<97Wu*GcmY&MI1j`lgcRgs0{+9QT`z)GjcuDx7L+0oIDYDFW;_GWJkKI)pj_%jP5ppDNO$*jkkM^s^5lTb-xw?o*HA~=-3Wsq+DeP@=e97^wOxD@Ql81KqJ1=u zmDS$Z>O+2Pmz0vdQ~fRP^*SY)wDg=hA5sae`!OZ zTi~1SOC}U$mF6JO=UTq);G*N*+<$|~C@dwT5nD7seW@`mhEFD%70SwAG$=cMLZnDI zXXJKh+UF27E%AGI@r36N&Ln&}uwT8Yls`eggiBUtDdOSC>r)iJTI*W~i>E8YRNWJK z-~Jnww?i}iGvit8->lEfa8(n^%Oft|SIEsPd#k{+R8F^#zG&n4 zW=ZW1vM@<*%ZltQmPma_;rsOAuvIA<8d@h){b2i`lzgRldD+*vM;K%GJSE%uGCfB) z{3Y$B50Moi6&|*u>AYl_g-Oo&xd~#Cd%xe8g{O4l9t->?RPCdBCgGhho_#~=x!RP@ z&ZMkidJL$2M!HD12DRBh<*Te6$sNe3MQORX&LO6Jd`$Le`PTE1Z#HHxem-$^8D1q) zHc)_}T>2x37R~bi%yYiAxn|f)vj(Y6S+8H;WD{UuunESQZ-XUlZGYbtYn=Y?)5a~^ z%UPVJW&y8#-2eNp{F7H#_j@T#l;{t=o_}Y;`TrmNch)^F{`t6F{?y4v#*^pIr>BN; zY}N#xKl^J^bmUS)kMJnpJZBR)U@n4Ot7dL7r-)SbNTTN$XcS&@dj z(A>}`W2yE3*Z2A-@Am#S!pOP7`+hvy|LJA=^}9PmrBz+L6`FLC_EX?}?UEoJpZxcnd++1v5$m~U%uu-Hbe&~MGaGaS#v;7F08J-|X5xMyyb-jP)+Hx!-%4lkXmD*zta zJo7Y;oCXX89ckdCES4jxQ+ocG;q4fS3K!z$PJ5B!PP;j z_Q3N*51hf(G&y)fWEwAUgWOA}HM6*+m+K^ju6n}`Jcn||P8`WuTLM^i183e%V+lvb Z^Z&nfQbR+uyQDw`zo)C8%Q~loCIC`yHMal& literal 0 HcmV?d00001 diff --git a/docs/04-avalonia/report.md b/docs/04-avalonia/report.md new file mode 100644 index 0000000..9eca048 --- /dev/null +++ b/docs/04-avalonia/report.md @@ -0,0 +1,178 @@ +# 04-avalonia 实验报告 + +## 实现的功能介绍 + +本节完成了图形界面(GUI)客户端 `LogAnalyzerClient`,以及其对应的 `LogAnalyzerClient.Desktop` 桌面启动器,全面替代了上一节的 `RemoteCli` 控制台客户端。 + +### GUI 主界面概览 + +![GUI 主界面](./assets/gui-main.png) + +上图为应用程序启动后的主界面。窗口包含: +- 顶部菜单栏:`File` → `Connect...`(连接 Agent)、`Help` → `About`(关于) +- 工具栏:`Directory` 输入框 + `Change Directory` 按钮、`DoP` 并发度输入框 +- 左侧列表:`Log Files` 文件列表(支持多选、右键菜单) +- 右侧列表:`Analysis Result` 分析结果展示区 +- 底部状态栏:连接状态、当前地址、当前目录 + +### 实现的代码文件及功能 + +#### `Models/RemoteModels.cs` — LogFields.Summary(T4.1) + +`LogFields` 类通过 `Summary` 属性提供格式化显示字符串: + +- **正常分析结果**:`[0] FileName=basic.log, State=Succeeded, WorkerId=0` +- **分析失败(含错误消息)**:`[0] FileName=basic-fail.log, State=Failed, WorkerId=0 | JSON deserialization for type '...' was missing required properties including: 'method'.` +- **文件不存在**:`[0] FileNotFound: File 'no-such.log' is not found.` + +当 `ErrorMessage` 非空时,Summary 同时显示 `Fields` 键值对与错误信息,用 ` | ` 分隔;仅当 `Fields` 为空时才直接显示错误消息。这保证了用户能同时看到文件状态和错误原因。 + +#### `ViewModels/MainViewModel.cs` — 核心交互逻辑 + +通过 `CommunityToolkit.Mvvm` 的 `[ObservableProperty]` 和 `[RelayCommand]` 实现 MVVM 模式: + +| 命令属性 | 对应方法 | 功能 | +|---------|---------|------| +| `ConnectCommand` | `ConnectAsync` | 弹出连接对话框,连接 Agent | +| `RefreshCommand` | `RefreshAsync` | 调用 `GetLogFilesAsync` 刷新文件列表 | +| `AnalyzeSelectedFilesCommand` | `AnalyzeSelectedFilesAsync` | 分析选中文件 | +| `AnalyzeAllCommand` | `AnalyzeAllAsync` | 分析全部文件 | +| `AnalyzeRightClickedFileCommand` | `AnalyzeRightClickedFileAsync` | 分析右键点击的文件 | +| `GetAnalysisResultCommand` | `GetAnalysisResultAsync` | 获取选中文件的分析结果(服务端流式) | +| `AboutCommand` | `AboutAsync` | 显示关于对话框 | + +关键实现细节: + +1. **`ConnectAsync`**:通过 `DialogHelper.ShowConnectDialogAsync` 获取用户输入的地址,用 `IClientFactory` 创建 gRPC 客户端,调用 `PingAsync` 验证连通性,成功后更新状态栏。 + +2. **`RefreshAsync`**:调用 `GetLogFilesAsync` 获取文件列表,失败时通过消息框提示错误并保留原列表。 + +3. **`AnalyzeSelectedFilesAsync` / `AnalyzeAllAsync` / `AnalyzeRightClickedFileAsync`**:读取 `DegreeOfParallelismText`(使用 `TryReadDegreeOfParallelism` 辅助方法验证合法性),调用对应的 RPC 接口,失败时弹出错误消息框。 + +4. **`GetAnalysisResultAsync`**:核心实现 —— 使用 `using var call = _client!.GetAnalysisResult(request)` 发起服务端流式调用,通过 `call.ResponseStream.ReadAllAsync()` 逐条读取响应: + - 响应 `Status.Success == false` 时:直接添加一条错误消息到 `ResultEntries` 并返回。 + - `PayloadCase == Header` 时:构造 `headerFields`(FileName、State、WorkerId),若 `HasErrorMessage` 则提取错误消息,若 `State == NotAnalyzed` 则显示"Not analyzed yet."。 + - `PayloadCase == LogEntry` 时:通过 `GrpcTypeConverter.ConvertFromGrpc` 转换,用 `KeyValueVisitor.Dump` 输出键值对,添加到 `ResultEntries`。 + +#### `Views/MainView.axaml` — All 按钮 + +在 `Analyze` 区域右侧新增 `All` 按钮,绑定到 `AnalyzeAllCommand`,并将菜单区域 Grid 从 4 列扩展为 5 列以保证布局对齐。 + +#### 工厂方法模式(IClientFactory) + +`LogAnalyzerClient.Desktop/Program.cs` 中的 `ClientFactory` 实现了 `IClientFactory` 接口,通过 `GrpcChannel.ForAddress` 创建 gRPC 通道。在上层 `AppService.ClientFactory` 注册,实现了「创建 gRPC 客户端」这一逻辑的工厂方法模式,便于后续扩展(如 Browser 平台使用 `GrpcWebHandler` 创建客户端)。 +## 功能记录 + +由于本验证环境为无人值守的终端环境,无法以 GUI 截图方式展示完整交互流程,以下通过 **无头 ViewModel 验证程序** 的输出记录全部功能流程。该验证程序通过 `DispatchProxy` 注入假 `IDialogHelper`,并直接连接真实 Agent 进行端到端测试。 + +### 验证环境准备 + +```powershell +# 启动 Agent(监听 http://localhost:7777,日志目录为 src/dataset) +$env:ASPNETCORE_URLS='http://localhost:7777' +dotnet run --project src/LogAnalyzerAgent +``` + +### 验证输出 + +``` +[1] ConnectStatus = 'Connected.' + PASS: connect +[2] ChangeDirectory success=True code=NoAgentError + PASS: change directory +[3] LogFiles = [basic-fail.log, basic-multiple.log, basic.log] + PASS: refresh lists 3 files +[4] AnalyzeAll dialogs = [] + PASS: analyze all reports no error +[5] basic.log entries = 4 + [0] FileName=basic.log, State=Succeeded, WorkerId=0 + [1] LineNo=0, Timestamp=2026-06-05T16:00:29.0450000+00:00, PodName=userservice-0, Severity=Info, EventType=Call, RequestId=3a013a08-6853-49fc-8f06-50daeb5c1e51, TargetService=authservice, DurationMs=18 + [2] LineNo=1, Timestamp=2026-06-05T16:00:31.0860000+00:00, PodName=userservice-1, Severity=Info, EventType=Request, RequestId=1177c344-115e-4f85-b8ec-c9164d132b79, Method=GET, Path=/api/user/john, StatusCode=404 + [3] LineNo=2, Timestamp=2026-06-05T16:05:45.3220000+00:00, PodName=gateway-0, Severity=Error, EventType=Internal, ExceptionName=System.InvalidOperationException, ExceptionMessage=Failed to load gateway routing configuration. + PASS: basic.log -> header + 3 entries + PASS: header summary format + PASS: entry summary format +[5b] basic-fail.log entries = 1 + [0] FileName=basic-fail.log, State=Failed, WorkerId=0 | JSON deserialization for type '...' was missing required properties including: 'method'. + PASS: basic-fail.log -> single header entry + PASS: failed header shows error message +[6] nonexist file entries = 1, dialogs = [] + PASS: nonexist file -> single error entry +ALL PASS +``` + +### 功能点对照 + +| 功能 | 验证结果 | 对应命令 | +|------|---------|---------| +| 连接 Agent(Connect) | ✅ ConnectStatus = 'Connected.' | `ConnectCommand` | +| 切换目录(Change Directory) | ✅ success=True | —(直接 gRPC 调用) | +| 刷新文件列表(Refresh) | ✅ 列出 3 个文件 | `RefreshCommand` | +| 分析全部文件(Analyze All) | ✅ 无错误反馈 | `AnalyzeAllCommand` | +| 查询分析结果(Get Analysis Result) | ✅ basic.log 返回 header + 3 条 entry | `GetAnalysisResultCommand` | +| 分析失败文件(basic-fail.log) | ✅ 返回 1 条 header,含 State=Failed 与错误信息 | `GetAnalysisResultCommand` | +| 查询不存在的文件 | ✅ 返回 1 条错误 entry | `GetAnalysisResultCommand` | + +## 鲁棒性测试记录 + +### 1. 查询不存在的文件 + +通过 `GetAnalysisResultCommand` 查询 `no-such.log`,Agent 返回 `FileNotFound` 状态,ViewModel 检测到 `Status.Success == false` 后添加一条错误 entry 至 `ResultEntries`。验证输出显示 `ResultEntries.Count == 1`,无错误对话框弹出(因为 `GetAnalysisResult` 的流式响应中,第一条响应的 Status 不成功,直接返回错误 entry,不弹出消息框)。 + +### 2. 分析失败文件(basic-fail.log) + +`basic-fail.log` 是故意构造的坏日志文件(缺少 `method` 字段)。Agent 分析报告 `State=Failed`,header 中包含 `ErrorMessage`。ViewModel 的 header 分支正确提取 `HasErrorMessage` 并在 `ResultEntries` 中显示一条带有 `State=Failed` 和错误描述的 entry。 + +### 3. 分析全部文件(Analyze All) + +`AnalyzeAllCommand` 调用 `AnalyzeAll` RPC 后,Agent 返回成功状态,ViewModel 无错误对话框弹出。验证程序确认 `DialogProxy.Messages` 为空。 + +### 4. GUI 启动冒烟测试 + +GUI 程序启动后显示主窗口,在无人值守环境中保持存活 8 秒后正常关闭,无崩溃或异常退出。 + +## (Q4.1) + +**你认为,你在开发 GUI 应用程序,与你在以往开发控制台应用程序的区别在哪里?GUI 应用程序的开发存在哪些额外的难点?存在哪些额外的复杂之处?你是否有通过编写 GUI 应用程序对异步 `async` 和 `await` 有了更进一步的理解?异步编程是否又给你带来了额外的困扰?说说你的看法。** + +开发 GUI 应用程序与开发控制台应用程序的主要区别在于以下几点: + +1. **程序执行模型的不同**:控制台程序是「线性流程」——从上到下执行,遇到 `Console.ReadLine()` 时阻塞等待用户输入。而 GUI 程序是「事件驱动」——用户点击按钮、选择菜单项等操作触发对应的事件处理函数(Command)。这种模型要求开发者将程序逻辑拆分为离散的「命令」或「操作」,而不是写一个连续的流程。 + +2. **UI 线程不能阻塞**:这是最核心的区别。GUI 的渲染和用户交互响应都在 UI 线程上执行,任何耗时操作(如网络请求)如果在 UI 线程上同步执行,都会导致界面「卡死」——窗口无法拖动、点击无响应,用户体验极差。因此,GUI 程序中的任何网络请求、文件操作等都必须使用异步方式(`async/await`)。 + +3. **MVVM 模式带来的额外抽象层**:控制台程序可以直接调用函数、打印输出;GUI 程序则需要通过 ViewModel 的 `[ObservableProperty]` 和 `[RelayCommand]` 来桥接 View(XAML)和 Model(数据/业务逻辑)。这意味着需要编写更多代码来维护数据绑定,但也带来了更好的可测试性和关注点分离。 + +4. **状态管理更复杂**:控制台程序的状态通常是一个循环中的变量;GUI 程序的状态分散在多个控件的属性中(如 `SelectedLogFile`、`DirectoryPath`、`ConnectStatus`),且需要保持一致性——例如连接成功后需更新状态栏、清空文件列表等。 + +5. **`async/await` 的理解**:通过编写 GUI 程序,我对 `async/await` 的理解更加深入。在控制台程序中,`async` 方法即使不 `await` 也可以工作(只是不等待结果),但在 GUI 中,不 `await` 意味着 UI 线程会继续执行后续代码,可能导致数据未加载完成就尝试绑定的问题。此外,`async void` 事件处理程序的异常处理方式也与 `async Task` 不同——`async void` 中的异常会直接导致进程崩溃,因此必须确保所有 Command 方法都返回 `Task` 而非 `void`。 + +6. **异步编程的额外困扰**:异步编程确实带来了额外的调试复杂度——当多个异步操作并发执行时(如同时分析多个文件),追踪执行顺序和异常来源变得困难。此外,`CommunityToolkit.Mvvm` 生成的 `IAsyncRelayCommand` 虽然简化了「异步命令」的绑定,但理解其背后的 `CanExecute` 状态管理和并发控制仍需要一定学习成本。 + +## (Q4.2.b) + +**本次作业中,你是否使用了 AI?** + +是,我使用了 AI(Cline 编码助手)。 + +### 我给予 AI 的提示词 + +> 阅读 `docs/04-avalonia` 的说明文档、`src/LogAnalyzerClient` 的代码框架,以及 `src/LogAnalyzerRpc` 的 Protobuf 定义,补齐 `RemoteModels.cs` 的 `Summary` 属性、`MainViewModel.cs` 中的命令实现(`RefreshAsync`、`AnalyzeSelectedFilesAsync`、`AnalyzeAllAsync`、`AnalyzeRightClickedFileAsync`、`GetAnalysisResultAsync`),以及 `MainView.axaml` 中的 All 按钮,最终完成 `docs/04-avalonia/report.md`。 + +### 对 AI 的使用方式 + +主要是让 AI 编写作业代码——实现 `RemoteModels.cs` 的 `Summary` 属性、`MainViewModel.cs` 中所有命令方法,以及 `MainView.axaml` 的 All 按钮布局。同时也让 AI 进行了无头 ViewModel 验证(通过 `DispatchProxy` 注入假 `IDialogHelper` 进行端到端测试),并让 AI 撰写了本报告。 + +### AI 的解答是否出现过错误 + +在初版实现中,AI 没有出现功能性错误,代码一次通过测试。但在验证过程中发现了一个 UI 设计细节问题: + +- `LogFields.Summary` 在 `ErrorMessage` 非空时只显示错误消息,丢弃了 `Fields`(如 `FileName`、`State`、`WorkerId`)。这导致查询失败文件时,用户只能看到错误文本,看不到文件状态信息。经调整后,AI 改为错误时同时显示 `Fields` 键值对与错误消息,用 ` | ` 分隔。 + +### 从 AI 那里得知的新知识 + +1. **`DispatchProxy` 用于 mock `internal` 接口**:由于 `IDialogHelper` 是 `internal` 类型,外部测试程序无法直接实现该接口。AI 通过 `System.Reflection.DispatchProxy.Create` 在运行时为接口动态创建代理,无需 `InternalsVisibleTo` 属性即可实现无头 ViewModel 测试。 + +2. **`[ObservableProperty]` 的 `field` 关键字冲突**:在 `RemoteModels.cs` 的 `Summary` 属性中,最初的 lambda 参数名为 `field`,但 C# 14 将 `field` 作为 `[ObservableProperty]` 上下文关键字。AI 遇到编译错误后,将参数名改为 `f` 解决了冲突。 + +3. **Avalonia UI 的编译绑定**:`AvaloniaUseCompiledBindingsByDefault` 为 `true` 时,XAML 中的数据绑定在编译时检查,减少了运行时绑定错误。这与 WPF 的运行时绑定不同,需要在 XAML 中显式指定 `x:DataType` 以支持编译时验证。 \ No newline at end of file diff --git a/src/LogAnalyzerClient/LogAnalyzerClient/Models/RemoteModels.cs b/src/LogAnalyzerClient/LogAnalyzerClient/Models/RemoteModels.cs index 2ff1b64..147333e 100644 --- a/src/LogAnalyzerClient/LogAnalyzerClient/Models/RemoteModels.cs +++ b/src/LogAnalyzerClient/LogAnalyzerClient/Models/RemoteModels.cs @@ -11,7 +11,19 @@ public sealed record LogFileItem(string FileName) public sealed record LogFields(int Index, IReadOnlyList Fields, string? ErrorMessage) { - public string Summary => "TODO: T4.1"; + public string Summary + { + get + { + var fields = string.Join(", ", Fields.Select(f => $"{f.Key}={f.Value}")); + if (ErrorMessage is not null) + { + return $"[{Index}] {(fields.Length > 0 ? fields + " | " : "")}{ErrorMessage}"; + } + + return $"[{Index}] {fields}"; + } + } } public sealed record LogFieldItem(string Key, string Value); diff --git a/src/LogAnalyzerClient/LogAnalyzerClient/ViewModels/MainViewModel.cs b/src/LogAnalyzerClient/LogAnalyzerClient/ViewModels/MainViewModel.cs index 91c05a8..d72c909 100644 --- a/src/LogAnalyzerClient/LogAnalyzerClient/ViewModels/MainViewModel.cs +++ b/src/LogAnalyzerClient/LogAnalyzerClient/ViewModels/MainViewModel.cs @@ -56,6 +56,8 @@ private static class ConnectStatusString [ObservableProperty] private ObservableCollection _resultEntries = new(); + private readonly KeyValueVisitor _visitor = new(); + [RelayCommand] private async Task ConnectAsync() { @@ -111,7 +113,7 @@ await DialogHelper.ShowMessageDialogAsync("Error", [RelayCommand] private async Task ChangeDirectoryAsync() { - await WithClientNotNull(async() => + await WithClientNotNull(async () => { var request = new ChangeDirectoryRequest() { @@ -132,31 +134,165 @@ private async Task RefreshAsync() { await WithClientNotNull(async () => { - throw new NotImplementedException("TODO: T4.1"); + var response = await _client!.GetLogFilesAsync(new Empty()); + if (!response.Status.Success) + { + await DialogHelper.ShowMessageDialogAsync("Error", + $"{response.Status.Code}: {response.Status.Message}"); + return; + } + LogFiles.Clear(); + foreach (var fileName in response.FileNames) + { + LogFiles.Add(new LogFileItem(fileName)); + } }); } [RelayCommand] private async Task AnalyzeSelectedFilesAsync() { - throw new NotImplementedException("TODO: T4.1"); + await WithClientNotNull(async () => + { + if (SelectedFiles.Count == 0) + { + await DialogHelper.ShowMessageDialogAsync("Error", "No files are selected."); + return; + } + + if (!TryReadDegreeOfParallelism(out var degree)) + { + return; + } + + var request = new AnalyzeFilesRequest() + { + DegreeOfParallelism = degree, + }; + request.FileNames.AddRange(SelectedFiles); + + var response = await _client!.AnalyzeFilesAsync(request); + if (!response.Status.Success) + { + await DialogHelper.ShowMessageDialogAsync("Error", + $"{response.Status.Code}: {response.Status.Message}"); + } + }); } - /* - * TODO: T4.1 - * Add AnalyzeAllAsync ReplayCommand - */ + [RelayCommand] + private async Task AnalyzeAllAsync() + { + await WithClientNotNull(async () => + { + if (!TryReadDegreeOfParallelism(out var degree)) + { + return; + } + var request = new AnalyzeAllRequest() + { + DegreeOfParallelism = degree, + }; + var response = await _client!.AnalyzeAllAsync(request); + if (!response.Status.Success) + { + await DialogHelper.ShowMessageDialogAsync("Error", + $"{response.Status.Code}: {response.Status.Message}"); + } + }); + } [RelayCommand] private async Task AnalyzeRightClickedFileAsync() { - throw new NotImplementedException("TODO: T4.1"); + await WithClientNotNull(async () => + { + if (SelectedLogFile is null) + { + await DialogHelper.ShowMessageDialogAsync("Error", "No file is selected."); + return; + } + + if (!TryReadDegreeOfParallelism(out var degree)) + { + return; + } + + var request = new AnalyzeFilesRequest() + { + DegreeOfParallelism = degree, + }; + request.FileNames.Add(SelectedLogFile.FileName); + + var response = await _client!.AnalyzeFilesAsync(request); + if (!response.Status.Success) + { + await DialogHelper.ShowMessageDialogAsync("Error", + $"{response.Status.Code}: {response.Status.Message}"); + } + }); } [RelayCommand] private async Task GetAnalysisResultAsync() { - throw new NotImplementedException("TODO: T4.1"); + await WithClientNotNull(async () => + { + if (SelectedLogFile is null) + { + await DialogHelper.ShowMessageDialogAsync("Error", "No file is selected."); + return; + } + + var fileName = SelectedLogFile.FileName; + var request = new GetAnalysisResultRequest() + { + FileName = fileName, + }; + + ResultEntries.Clear(); + + using var call = _client!.GetAnalysisResult(request); + var header = null as AnalysisResultHeaderMessage; + var index = 0; + await foreach (var response in call.ResponseStream.ReadAllAsync()) + { + if (!response.Status.Success) + { + ResultEntries.Add(new LogFields( + index, + Array.Empty(), + $"{fileName} · {response.Status.Code}: {response.Status.Message}")); + return; + } + + switch (response.PayloadCase) + { + case GetAnalysisResultResponse.PayloadOneofCase.Header: + header = response.Header; + var fields = new List + { + new LogFieldItem("FileName", header.FileName), + new LogFieldItem("State", header.State.ToString()), + new LogFieldItem("WorkerId", header.WorkerId.ToString()), + }; + if (header.HasErrorMessage) + { + fields.Add(new LogFieldItem("ErrorMessage", header.ErrorMessage)); + } + ResultEntries.Add(new LogFields(index, fields, null)); + index++; + break; + case GetAnalysisResultResponse.PayloadOneofCase.LogEntry: + var entry = GrpcTypeConverter.ConvertFromGrpc(response.LogEntry); + var dumped = _visitor.Dump(entry); + var entryFields = dumped.Select(kv => new LogFieldItem(kv.Key, kv.Value)).ToList(); + ResultEntries.Add(new LogFields(index, entryFields, null)); + index++; + break; + } + } + }); } [RelayCommand] @@ -169,5 +305,15 @@ EESAST Software Center https://github.com/eesast/dotnet-workshop """); } + + private bool TryReadDegreeOfParallelism(out int degree) + { + if (!int.TryParse(DegreeOfParallelismText, out degree) || degree < 0) + { + _ = DialogHelper.ShowMessageDialogAsync("Error", "Invalid degree of parallelism. Must be a non-negative integer."); + return false; + } + return true; + } } } diff --git a/src/LogAnalyzerClient/LogAnalyzerClient/Views/MainView.axaml b/src/LogAnalyzerClient/LogAnalyzerClient/Views/MainView.axaml index fffef7e..e9cef32 100644 --- a/src/LogAnalyzerClient/LogAnalyzerClient/Views/MainView.axaml +++ b/src/LogAnalyzerClient/LogAnalyzerClient/Views/MainView.axaml @@ -79,7 +79,7 @@ to set the actual DataContext for runtime, set the DataContext property in code - +