Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,6 @@ FodyWeavers.xsd

# JetBrains Rider
*.sln.iml
.obsidian/

.zcode/
75 changes: 75 additions & 0 deletions docs/01-basic/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
## 问答题

### (Q1.1)

在给出的代码框架 `Parser` 中:

+ 哪条语句或哪几条语句将日志按逗号进行分割?代码中,我们是如何指定每一行的第几个字段代表何种意义的?

```c
// 按逗号进行分割
using var csv = new CsvReader(logFile, config);
csv.Context.RegisterClassMap<LogRecordMap>();
foreach (var logRecord in csv.GetRecords<LogRecord>())
```

通过 LogRecordMap 指定意义;

+ 在对日志中 JSON 格式的 `message` 字段进行读取时,我们是在哪个方法内用哪几条语句判断这一行日志的种类(Call / Request / Internal)的?

```c
// 通过 LineRarse 的 ParseLine 方法

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($"Unknown event type: {eventElement.GetString()} in log message: {logRecord.Message}")
};
}
else
{
throw new FormatException($"Log message does not contain 'event' property: {logRecord.Message}");
}

```

+ 在确定了日志种类后,我们是调用了哪个库方法对 JSON 进行解析的?

用 System.Text.Json 的 JsonSerializer.Deserialize<T>(logRecord.Message, options)

+ 进一步,我们的框架代码是如何防止日志中有字段缺失的?(例如所给的 Call 日志的 `message` 中缺失 `request_id` 字段)

使用 [property: JsonRequired]

+ 更进一步,日志中的 JSON 的键是 `abc-def` 命名法(称为烤串命名法),而我们的解析结果却是放在 `AbcDef` 命名法(称为大驼峰命名法)的属性里,我们的框架代码中是如何告诉 JSON 解析器完成这一命名法转换的?

```c
// 使用 JsonSerializerOptions
private static JsonSerializerOptions options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower,
};
```

### (Q1.2)

以一个 Call 事件的解析结果为例,当调用 `KeyValueVisitor` 的 `Dump` 方法后,都有哪些方法被调用?请补充完整如下的方法调用链(.NET 内置库无需写出):

+ `Dictionary<string, string> KeyValueVisitor.Dump(LogEntry entry)`
+ `TResult CallLogEntry.Accept<TResult>(ILogEntryVisitor<TResult> visitor)`

+ `Dictionary<string, string> KeyValueVisitor.Visit(CallLogEntry entry)`

### (Q1.3)

#### (Q1.3.b)

如果使用了 AI,你给予 AI 的提示词是什么?你认为 AI 给出的解答、你完全凭借传统搜索引擎以及自己的能力能够写出的解答之间,AI 的解答比你好在哪?AI 又有哪些解答是存在问题的,或者至少是不如你自己的解答的?给出你的理由。

- 使用的是 copilot 补全代码,以及借助 codex 理解代码;

- 我让 codex 理解一下 01-basic 代码部分已有的工作;然后自己阅读代码的时候借助 copilot 补全注释的能力理解一些看不明白的语法以及补全补全一些重复的代码;但是 copilot 的补全能力不像 codex 之类的整个仓库一起阅读好像不太能理解代码上下文和所文件情况比如对于对于 Call, Request 之类不同 Json 字段他的补充方式就是完全照搬原本写的,所以还是得靠自己 review 完动手调()
45 changes: 45 additions & 0 deletions docs/02-multithreading/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
## 问答题

问答题的提交方式是在 `docs/02-multithreading` 中的 `report.md` 文件中进行你对问题的解答。

### (Q2.1)

本问题考察关于临界区的理解。

我们把访问临界资源的程序片段称作临界区。在我们的多线程程序当中,临界资源即为不同线程的共享变量。请问:

+ `WorkQueue<T>` 类中的共享变量有哪些?是通过什么保护其免于数据竞争(data race)呢?

_isCompleted and _items,通过 lock

+ `LogFileAnalyzer` 类中的共享变量有哪些?是通过什么保护其免于数据竞争呢?

_currentDirectory、_isAnalyzing、_logFiles、_analysisResults,通过 lock

+ 如果条件变量的判断条件使用了 `if` 判断而非 `while` 判断,当出现了虚假唤醒现象时(在类 UNIX 系统中,由于 UNIX 信号等机制,即使没有人调用过 `signal` 或 `broadcast`,处于 `wait` 当中的条件变量也可能被唤醒),会出现什么后果?结合无限仓库容量的生产者消费者问题简单叙述一下。

可能会对空队列 Dequeue 抛出异常

### (Q2.2)

在给出的代码框架 `LogFileAnalyzer` 中:

+ 那一段代码扫描了给定的目录中的全部 `.log` 后缀的日志文件?

ChangeDirectory 方法

+ 假使给定的需求是不但要扫描给定目录中的日志文件,还要递归地获取给定的目录的全部子目录、子子目录……内的日志文件,应当如何做(简要回答即可)?

可以扫描的时候多判断一次该目录下的每一个文件、对于日志文件解析,对于目录文件同样调用扫描函数递归进入该子目录扫描

### (Q2.3)

#### (Q2.3.b)

如果使用了 AI,你给予 AI 的提示词是什么?你对 AI 的使用是询问 AI 一些接口的用法或是在某处的写法,还是让 AI 帮你写一部分作业代码,又或是让 AI 给你讲解代码框架?AI 的解答是否出现过错误(如果有,是哪些)?你认为本节的难度是偏低、适中,还是偏高?

仍然是让他分析代码,对于不熟悉的命名法,讲解每个文件和类是做什么的,另外补全一些简单代码;

出现过错误,比如实现简单 CLI 时补全输出的显示代码的时候由于跨文件所以补写出一些不存在的属性比如 LogEntry 中出现不存在的 Message 属性

有些难度,感觉仍然不熟悉 C# 语法(x
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions docs/03-async-grpc/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
![[Pasted image 20260830144545.png]]![[Pasted image 20260830144609.png]]

## 问答题

### (Q3.1)

你认为,你在开发网络应用程序,与你在以往开发非网络应用程序的区别在哪里?网络应用程序的开发存在哪些额外的难点?存在哪些额外的复杂之处?

LocalCli 是全在一个进程里。这次调用 client.AnalyzeFilesAsync(),服务端在另一个进程,中间隔着网络。

我感觉那些要知道数据那些自动生成的方法和属性很麻烦,得去翻 proto 文件或 obj 里生成的代码(`FileNames` 还是 `LogFiles`、oneof 生成 `PayloadCase`)

### (Q3.2)

本次作业中,你是否使用了 AI?根据你的使用情况,在以下 (Q3.2.a) (Q3.2.b) 两个问题中选择一题作答:

#### (Q3.2.b)

帮我解释一些接口,帮我讲解那些 proto 自动生成的那些数据类型的属性和方法,也讲解一些框架,感觉仍然不是很明白,有一种被 ai 牵着走写代码的感觉(悲),仍然不太记得住和理解那些 C# 中的复杂的属性方法之类的名称和用法(晕),只能一边写一边求助 ai(

深刻的感觉到了我拖累了 ai 的水平(

Binary file added docs/04-avalonia/assets/report-01-xxx.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/04-avalonia/assets/report-02-xxx.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/04-avalonia/assets/report-03-xxx.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions docs/04-avalonia/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## 功能实现


图形界面客户端 LogAnalyzerClient 的如下功能(T4.1):

1. 刷新文件列表(Refresh):调用 `GetLogFiles` RPC 获取服务器当前目录的日志文件,清空并重建 `LogFiles` 集合,界面列表自动刷新。菜

2. 分析选中的文件(Selected):Ctrl 多选文件后点击 Selected,调用 `AnalyzeFiles` RPC,请求中带上并行度和选中的文件名列表;并行度会先做整数校验。

3. 分析全部文件(All):在 Analyze 栏新增 All 按钮(axaml 加第五列),绑定新写的`AnalyzeAllCommand`,调用 `AnalyzeAll` RPC。

4. 分析右键选中的文件(Analyze File):右键菜单触发 `AnalyzeRightClickedFileCommand`,分析当前选中的单个文件。

5. 查看分析结果(View Analysis Results):右键菜单触发,使用流式 RPC `GetAnalysisResult`

## 测试截图

![[report-01-xxx.png]]

![[report-02-xxx.png]]
![[report-03-xxx.png]]
Comment on lines +18 to +21
## 问答题

### (Q4.1)

你认为,你在开发 GUI 应用程序,与你在以往开控制台应用程序的区别在哪里?GUI 应用程序的开发存在哪些额外的难点?存在哪些额外的复杂之处?你是否有通过编写 GUI 应用程序对异步 `async` 和 `await` 有了更进一步的理解?异步编程是否又给你带来的额外的困扰?说说你的看法。

它需要一些 UI 设计的品味,如何布局等,需要确认耦合关系;

但是我对于异步编程以及其他模块中自动生成编译的方法属性仍然很不熟悉,不太清楚它的使用方法和原理,导致在调试时遇到了一些问题。包括命名、对应关系、如何在程序中定位某个方法和属性并 F12 定位后完之后如何看懂它都遇到了困难

### (Q4.2)

#### (Q4.2.b)

如果使用了 AI,你给予 AI 的提示词是什么?你对 AI 的使用是询问 AI 一些接口的用法、gRPC 的使用,或是在某处的写法,还是让 AI 帮你写一部分作业代码,又或是让 AI 给你讲解代码框架?AI 的解答是否出现过错误(如果有,是哪些)?你从 AI 那里是否得知了一些关于异步,或是 gRPC 等原本你不知道或是难以理解的知识?

仍然是让 AI 讲解代码框架,询问一些参数的使用方法,帮我确认代码问题和 Debug,处理一些不明白的报错,对于一些不太清楚的接口写法进行确认。
42 changes: 21 additions & 21 deletions docs/appendix/appendix-a-glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,27 +40,27 @@

## 云服务与日志

| 简体中文(大陆) | 繁體中文(台灣) | English (United States) |
| --- | --- | --- |
| 云 | 雲端 | cloud |
| 云服务 | 雲端服務 | cloud service |
| 微服务 | 微服務 | microservice |
| 架构 | 架構 | architecture |
| 部署 | 部署 | deployment |
| 前端 | 前端 | frontend |
| 后端 | 後端 | backend |
| 日志 | 記錄 | log |
| 日志文件 | 記錄檔 | log file |
| 结构化日志 | 結構化記錄 | structured log |
| 半结构化日志 | 半結構化記錄 | semi-structured log |
| 非结构化日志 | 非結構化記錄 | unstructured log |
| 日志等级 | 記錄層級 | log level |
| 字段 | 欄位 | field |
| 故障诊断 | 故障診斷 | fault diagnosis |
| 异常检测 | 異常偵測 | anomaly detection |
| 大语言模型 | 大型語言模型 | large language model (LLM) |
| 可观测性 | 可觀測性 | observability |
| 云服务拓扑 | 雲端服務拓撲 | cloud service topology |
| 简体中文(大陆) | 繁體中文(台灣) | English (United States) |
| -------- | -------- | -------------------------- |
| 云 | 雲端 | cloud |
| 云服务 | 雲端服務 | cloud service |
| 微服务 | 微服務 | microservice |
| 架构 | 架構 | architecture |
| 部署 | 部署 | deployment |
| 前端 | 前端 | frontend |
| 后端 | 後端 | backend |
| 日志 | 記錄 | log |
| 日志文件 | 記錄檔 | log file |
| 结构化日志 | 結構化記錄 | structured log |
| 半结构化日志 | 半結構化記錄 | semi-structured log |
| 非结构化日志 | 非結構化記錄 | unstructured log |
| 日志等级 | 記錄層級 | log level |
| 字段 | 欄位 | field |
| 故障诊断 | 故障診斷 | fault diagnosis |
| 异常检测 | 異常偵測 | anomaly detection |
| 大语言模型 | 大型語言模型 | large language model (LLM) |
| 可观测性 | 可觀測性 | observability |
| 云服务拓扑 | 雲端服務拓撲 | cloud service topology |

## C\#、面向对象程序设计与设计模式

Expand Down
113 changes: 108 additions & 5 deletions src/LocalCli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,22 +112,125 @@ 6. Exit.

private static void ShowLogFiles(LogFileAnalyzer analyzer)
{
throw new NotImplementedException("T2.3");
var logFiles = analyzer.GetLogFiles();
if (logFiles.Count == 0)
{
Console.WriteLine("No log files found.");
return;
}
Console.WriteLine("Log files:");
foreach (var file in logFiles)
{
Console.WriteLine($"- {file}");
}
}

private static void AnalyzeFiles(LogFileAnalyzer analyzer)
{
throw new NotImplementedException("T2.3");
Console.WriteLine("Enter the degree of parallelism:");
int degreeOfParallelism = 0;
Console.Write(">>> ");
var degreeStr = Console.ReadLine();
if (degreeStr is null)
{
Console.WriteLine("No degree of parallelism entered.");
return;
}
try
{
degreeOfParallelism = int.Parse(degreeStr);
}
catch (Exception)
{
Console.WriteLine("Invalid input, please try again.");
return;
}
Console.WriteLine("please input log file names (comma separated):");
Console.Write(">>> ");
var fileNamesStr = Console.ReadLine();
if (fileNamesStr is null)
{
Console.WriteLine("No file names entered.");
return;
}
var fileNames = fileNamesStr.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
try
{
analyzer.AnalyzeFiles(degreeOfParallelism, 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");
Console.WriteLine("Enter the degree of parallelism:");
int degreeOfParallelism = 0;
Console.Write(">>> ");
var degreeStr = Console.ReadLine();
if (degreeStr is null)
{
Console.WriteLine("No degree of parallelism entered.");
return;
}
try
{
degreeOfParallelism = int.Parse(degreeStr);
}
catch (Exception)
{
Console.WriteLine("Invalid input, please try again.");
return;
}
try
{
analyzer.AnalyzeAll(degreeOfParallelism);
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 log file name:");
Console.Write(">>> ");
var fileName = Console.ReadLine();
if (fileName is null)
{
Console.WriteLine("No file name entered.");
return;
}
try
{
if (!analyzer.TryGetAnalysisResult(fileName, out var result) || result is null)
{
Console.WriteLine($"File '{fileName}' not found in the current directory.");
return;
}
Console.WriteLine($"Analysis result for {fileName}:");
Console.WriteLine($"- State: {result.State}");
if (result.State == AnalysisState.Failed)
{
Console.WriteLine($"- Error message: {result.ErrorMessage}");
}
else
{
Console.WriteLine($"- Number of entries: {result.Entries.Count}");
foreach (var entry in result.Entries)
{
Console.WriteLine($" - {entry.Timestamp}");
}
}
Comment on lines +215 to +228
}
catch (Exception ex)
{
Console.WriteLine($"Error getting analysis result: {ex.Message}");
}
}
}
}
Loading
Loading