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
74 changes: 74 additions & 0 deletions docs/01-basic/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
## Q 1.1
### 1.1.1
- 以下代码负责按逗号分割:
```c sharp
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false
};
using var csv = new CsvReader(logFile, config);
csv.Context.RegisterClassMap<LogRecordMap>();
```
- 经过CSV分割后,每一行的文本通过以下代码对应到LineNo、Timestamp、PodName、Message四个字段
```c sharp
public LogRecordMap()
{
Map(m => m.LineNo).Index(0);
Map(m => m.Timestamp).Index(1);
Map(m => m.PodName).Index(2);
Map(m => m.Message).Index(3);
}
```
,存为一个LogRecord对象,之后单独`JsonDocument.Parse(logRecord.Message)`提取`event`类型,交由`LineParser.CreateCall(logRecord)`等方法,按照`LogEntries.cs`中的定义映射各自含义
### 1.1.2
- 在`LineParser::ParseLine(LogRecord logRecord)`中:
```c sharp
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}");
}
```
### 1.1.3
- 使用json库:
```c sharp
using System.Text.Json.Serialization;
...
JsonSerializer.Deserialize<CallMessage>(logRecord.Message, options);
...
```

- 使用`[property: JsonRequired]`强制指定,不存在时报错
- 通过options传入源文本的编码方式Kebab
``` c sharp
private static JsonSerializerOptions options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower,
};
...
var callMessage = JsonSerializer.Deserialize<CallMessage>(logRecord.Message, options)
...

```
驼峰命名法在LogEntry的子类中分别指定。

## Q1.2
- `Dictionary<string, string> KeyValueVisitor.Dump(LogEntry entry)`
- `TResult LogEntry.Accept<TResult>(ILogEntryVisitor<TResult> visitor)`
- `TResult CallLogEntry.Accept<TResult>(ILogEntryVisitor<TResult> visitor)`
- `Dictionary<string, string> Visit(CallLogEntry entry)`

## Q1.3
- 有使用
### Q1.3.b
- 主要使用了Copilot的自动补全,相比我自己写更省时间,减少了排错成本,经测试核查无误
- 也让ai解释了一些语句的含义、函数的用法
25 changes: 25 additions & 0 deletions docs/02-multithreading/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Q2.1
### 2.1.1
- WorkQueue共享变量有:`_items`、`_isCompleted`
- `_items`通过`lock(_items){...}`防止竞争;
- `_isCompleted`通过每次读取时`lock (_items)`保证读到的值正确,写入时`lock (_items)`保证单一线程访问
- LogFileAnalyzer的共享变量有:`_analysisResults`、`_isAnalyzing`、`_logFiles`、`_currentDirectory`
- `_analysisResults`、`_logFiles`通过`lock(_syncRoot){...}`保护
- `_isAnalyzing`通过每次读取时`lock (_syncRoot)`保证读到的值正确
- `_currentDirectory`写入在 `lock (_syncRoot)` 中,读取未加锁,不过其一般也不会被多个线程写入
- 不用while的话,wait被唤醒后就继续向下执行了,就像顾客没有等到生产者通知生产完成就尝试去仓库抢东西,可能产生不符合预期的bug
## Q2.2
- 扫描全部 .log 后缀的日志文件:
```csharp
var logFiles = Directory.EnumerateFiles(directoryPath, "*.log", SearchOption.TopDirectoryOnly)
.Select(filePath => Path.GetFileName(filePath))
.OrderBy(fileName => fileName);
foreach (var fileName in logFiles){...}
```
- `SearchOption.AllDirectories`
## Q2.3
- 有使用
### 2.3.b
- 主要使用Copilot的自动补全,以及让ai解释一些语句的含义、函数的用法,提示词就是“解释xxx的参数含义与用法”
- 由于自动补全是顺着我的思路写的,可能我起头起偏了,ai写的也会出问题,比如`worker.Join()`的时候没有注意到这个函数会自动阻塞,就写了个while,然后copilot就顺着开编了,这时就需要另外让ai解释一下函数用法,检查一下逻辑是否合理,多给几个角度再自行核对
- 本节难度适中
Binary file added docs/03-async-grpc/image-1.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/03-async-grpc/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions docs/03-async-grpc/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
- 普通测试
![alt text](image.png)
- 稳健性测试
![alt text](image-1.png)
## Q3.1
- 最大的区别是需要考虑网络通信的延迟、丢包、超时等各种未知情况,这些情况都需要服务端和客户端处理。这也使得异步、握手、状态码等机制在网络通信中得到广泛应用
- 难点与复杂之处主要在于,需要处理网络通信的不稳定性,除此之外还要考虑前后端通信接口的一致性、可维护性,以及服务器在大量访问请求下的稳定性等
## Q3.2
有使用
### 3.2.b
- AI主要负责自动补全,解释一些语句、接口的用法,以及帮忙找bug
- 提示词:解释一下xxx的用法与参数含义
- 至少在自动补全方面,AI经常捏造出各种各样实际不存在的字段或参数,比如往`var request = new ChangeDirectoryRequest()`的构造函数加了个不知哪来的`Force = true`,这时就只能翻定义
- 确有了解更多知识,比如CancellationToken,在gRPC中可以让服务端按需取消某些任务,比如客户端断连时触发,防止服务端做无用功
Binary file added docs/04-avalonia/image-1.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/image-2.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/image-3.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/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions docs/04-avalonia/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
![alt text](image.png)
![alt text](image-1.png)
![alt text](image-2.png)
![alt text](image-3.png)

## Q4.1
- 感觉GUI界面最大的特点就是用户与程序的交互变得多元化了,比如光一个按钮就可能有左键、右键、点击、长按、拖动等多种交互方式。并且UI设计的层次性、美观性也有要求。如何让UI交互直观、顺手、不卡顿闪退,都是难点。
- 编程过程中更加体会到了异步编程的重要性,UI的流畅性无比重要,不能每点一下就卡一会儿
- 异步编程并非顺序运行,因此经常感到程序的时序混乱,比如此处要不要等?能容忍多久的延迟?返回之后如何处理?都会带来一些困惑
## Q4.2
有使用
- AI主要负责自动补全,解释一些语句、接口的用法,以及帮忙找bug
- 提示词:解释一下xxx的用法与参数含义/根据04-avalonia中的指南,看看我刚写的ui实现是否正确稳健,之后检查一下xxx的报错原因
- 至少在自动补全方面,AI经常捏造出各种各样实际不存在的字段或参数,比如往`var request = new ChangeDirectoryRequest()`的构造函数加了个不知哪来的`Force = true`,这时就只能翻定义
- 确有了解更多知识,比如CancellationToken,在gRPC中可以让服务端按需取消某些任务,比如客户端断连时触发,防止服务端做无用功
70 changes: 66 additions & 4 deletions src/LocalCli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,14 @@
switch (choice)
{
case 1:
actions[choice](analyzer);
break;
case 2:
actions[choice](analyzer);
break;
case 3:
actions[choice](analyzer);
break;
case 4:
actions[choice](analyzer);
break;
Expand All @@ -112,22 +118,78 @@

private static void ShowLogFiles(LogFileAnalyzer analyzer)
{
throw new NotImplementedException("T2.3");
analyzer.GetLogFiles().ToList().ForEach(fileName => Console.WriteLine(fileName));
}

private static void AnalyzeFiles(LogFileAnalyzer analyzer)
{
throw new NotImplementedException("T2.3");
Console.WriteLine("Please input log file names, separated by space:");
var input = Console.ReadLine();
if (input is null)
{
return;
}
var fileNames = input.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

Console.WriteLine("Please input degree of parallelism:");
var degreeOfParallelismStr = Console.ReadLine();
if (degreeOfParallelismStr is null)
{
return;
}
if (!int.TryParse(degreeOfParallelismStr, out var degreeOfParallelism))
{
Console.WriteLine("Invalid input for degree of parallelism.");
return;
}
analyzer.AnalyzeFiles(degreeOfParallelism, fileNames);
Console.WriteLine("Done.");
}

private static void AnalyzeAll(LogFileAnalyzer analyzer)
{
throw new NotImplementedException("T2.3");
Console.WriteLine("Please input degree of parallelism:");
var degreeOfParallelismStr = Console.ReadLine();
if (degreeOfParallelismStr is null)
{
return;
}
if (!int.TryParse(degreeOfParallelismStr, out var degreeOfParallelism))
{
Console.WriteLine("Invalid input for degree of parallelism.");
return;
}
analyzer.AnalyzeAll(degreeOfParallelism);
Console.WriteLine("Done.");
}

private static void GetAnalysisResult(LogFileAnalyzer analyzer)
{
throw new NotImplementedException("T2.3");
analyzer.GetLogFiles().ToList().ForEach(fileName =>
{
if (analyzer.TryGetAnalysisResult(fileName, out var result))
{
if (result == null)
{
Console.WriteLine($"No analysis result for file: {fileName}");
}
Console.WriteLine($"File: {fileName}");
Console.WriteLine($"State: {result.State}");

Check warning on line 177 in src/LocalCli/Program.cs

View workflow job for this annotation

GitHub Actions / test-04-avalonia

Dereference of a possibly null reference.
if (result.State == AnalysisState.Failed)
{
Console.WriteLine($"Error Message: {result.ErrorMessage}");
}
else
{
Console.WriteLine($"Log Entries Count: {result.Entries.Count}");
}
Console.WriteLine();
}
else
{
Console.WriteLine($"No analysis result for file: {fileName}");
}
});
}
}
}
58 changes: 49 additions & 9 deletions src/LogAnalyzer/LogFileAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public void AnalyzeFiles(int degreeOfParallelism, IEnumerable<string> fileNames)
/*
* Set _isAnalyzing
*/
// TODO: T2.2
_isAnalyzing = true;
}

try
Expand All @@ -154,7 +154,10 @@ public void AnalyzeFiles(int degreeOfParallelism, IEnumerable<string> fileNames)
* Unset _isAnalyzing
* Remember to lock _syncRoot to prevent data race
*/
// TODO: T2.2
lock (_syncRoot)
{
_isAnalyzing = false;
}
}
}

Expand All @@ -169,7 +172,15 @@ private void RunWorkers(int degreeOfParallelism, IReadOnlyList<FileInfo> fileLis
* Filter unparsed files.
* If there is an unknown file, throw System.InvalidOperationException.
*/
throw new NotImplementedException("TODO: T2.2");
if (!_analysisResults.ContainsKey(file.Name))
{
throw new InvalidOperationException($"Unknown file: {file.Name}");
}
if (_analysisResults[file.Name].State == AnalysisState.NotAnalyzed)
{
logFilesToParse.Add(file);
}

}
}

Expand All @@ -183,7 +194,11 @@ private void RunWorkers(int degreeOfParallelism, IReadOnlyList<FileInfo> 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];
Expand All @@ -194,13 +209,21 @@ private void RunWorkers(int degreeOfParallelism, IReadOnlyList<FileInfo> fileLis
/*
* Create and start threads to run `WorkerMain`
*/
// TODO: T2.2
workers[i] = new Thread(() => WorkerMain(workerId, queue))
{
Name = threadName,
IsBackground = true
};
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<FileInfo> queue)
Expand All @@ -213,19 +236,36 @@ private void WorkerMain(int workerId, WorkQueue<FileInfo> queue)
try
{
// Parse file
throw new NotImplementedException("TODO: T2.2");
result = new AnalysisResult(
file.Name,
file.FullName,
AnalysisState.Succeeded,
parser.Parse(file.OpenText()).ToList(),
null,
workerId
);
}
catch (Exception ex)
{
// Save exception message to result
throw new NotImplementedException("TODO: T2.2");
result = new AnalysisResult(
file.Name,
file.FullName,
AnalysisState.Failed,
new List<LogEntry>(),
ex.Message,
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;
}
}
}
}
Expand Down
Loading
Loading