diff --git a/docs/01-basic/report.md b/docs/01-basic/report.md new file mode 100644 index 0000000..cefb067 --- /dev/null +++ b/docs/01-basic/report.md @@ -0,0 +1,23 @@ + 01-basic 问答题 +Q1.1 +1. 分割与映射:使用 `CsvHelper` 库的 `LogRecordMap` 类,通过 `Map(m => m.LineNo).Index(0)` 指定第 0 列为行号,第 1 列时间戳,第 2 列 Pod 名,第 3 列为 Message。 + +2. 类型判断:在 `LineParser.ParseLine` 方法中,通过 `JsonDocument.Parse` 解析 `Message`,再用 `TryGetProperty("event", out var eventElement)` 获取事件字段,最后用 `switch` 匹配 `"call"`、`"request"`、`"internal"`。 + +3. JSON 解析方法:调用 `System.Text.Json.JsonSerializer.Deserialize()` 将 JSON 转为强类型对象。 + +4. 防止字段缺失:在 `CallMessage`、`RequestMessage` 等 record 的属性上标注 `[JsonRequired]`,确保字段存在。 + +5. 命名转换:通过 `JsonSerializerOptions` 设置 `PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower`,自动映射 `RequestId` ↔ `request-id`。 + +Q1.2 + +调用链: +1. `KeyValueVisitor.Dump(LogEntry entry)` +2. `CallLogEntry.Accept(ILogEntryVisitor visitor)`(多态匹配实际类型) +3. `KeyValueVisitor.Visit(CallLogEntry entry)` + +Q1.3.b +交互过程:提供了题目和代码文件,要求进行逐行讲解和代码实现指导,并在环境配置(WSL 安装 .NET 10 SDK)上获得了操作命令帮助。 +AI 优点:AI 相比我自行查阅文档,AI 能结合具体任务提供更加定制化方案,节省了寻找合适 API 的时间。同时对代码可以有更详细地讲解,降低了我的理解门槛。 +AI不足:存在幻觉没看到的文档/内容也会自己生成。 \ No newline at end of file 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..dbedf90 100644 --- a/src/LogParser/Parser/LineParser.cs +++ b/src/LogParser/Parser/LineParser.cs @@ -16,9 +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"), - _ => throw new FormatException($"Unknown event type: {eventElement.GetString()} in log message: {logRecord.Message}") + "request" => LineParser.CreateRequest(logRecord), + "internal" => LineParser.CreateInternal(logRecord), }; } else @@ -48,14 +47,45 @@ 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}"); + + var exceptionFull = internalMessage.Exception; + var colonIndex = exceptionFull.IndexOf(':'); + if (colonIndex == -1) + throw new FormatException($"Exception format invalid, missing ':' in: {exceptionFull}"); + + var exceptionName = exceptionFull.Substring(0, colonIndex).Trim(); + var exceptionMessage = exceptionFull.Substring(colonIndex + 1).Trim(); + + return new InternalLogEntry( + LineNo: logRecord.LineNo, + Timestamp: DateTimeOffset.Parse(logRecord.Timestamp), + PodName: logRecord.PodName, + Severity: ParseSeverity(internalMessage.Severity), + ExceptionName: exceptionName, + ExceptionMessage: exceptionMessage + ); } private static LogSeverity ParseSeverity(string severity) @@ -78,10 +108,17 @@ 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..0e1cf46 100644 --- a/src/LogParser/Visitors/KeyValueVisitor.cs +++ b/src/LogParser/Visitors/KeyValueVisitor.cs @@ -1,4 +1,5 @@ using LogParser.Models; +using System.Collections.Generic; namespace LogParser.Visitors { @@ -24,14 +25,36 @@ public Dictionary Visit(CallLogEntry entry) }; } + // 新增:处理Request类型日志 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() + }; } + // 新增:处理 Internal 类型日志 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 + }; } } }