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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,4 @@ FodyWeavers.xsd

# JetBrains Rider
*.sln.iml
.obsidian/
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
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;
Comment on lines +139 to +146
}
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}");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error getting analysis result: {ex.Message}");
}
}
}
}
Loading
Loading