Update
This commit is contained in:
79
ConsoleApp2/Services/ErrorRecorder/ErrorRecorder.cs
Normal file
79
ConsoleApp2/Services/ErrorRecorder/ErrorRecorder.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using ConsoleApp2.Helpers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ConsoleApp2.Services.ErrorRecorder;
|
||||
|
||||
public class ErrorRecorder
|
||||
{
|
||||
protected ILogger Logger;
|
||||
|
||||
/// <summary>
|
||||
/// 当次执行标识
|
||||
/// </summary>
|
||||
public static readonly string UID = DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss");
|
||||
|
||||
public ErrorRecorder(ILogger logger)
|
||||
{
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
|
||||
public static async Task LogErrorRecordAsync(string outputDir, DataRecord record, Exception exception)
|
||||
{
|
||||
if(!Directory.Exists(outputDir))
|
||||
Directory.CreateDirectory(outputDir);
|
||||
var content = $"""
|
||||
### {exception.Message}
|
||||
{record.RawField}
|
||||
""";
|
||||
var path = Path.Combine(outputDir, $"{record.TableName}.errlog");
|
||||
await File.AppendAllTextAsync(path, content);
|
||||
}
|
||||
|
||||
public static async Task LogErrorRecordAsync(string outputDir, IEnumerable<DataRecord> records, Exception exception)
|
||||
{
|
||||
if(!Directory.Exists(outputDir))
|
||||
Directory.CreateDirectory(outputDir);
|
||||
var tableMapping = new Dictionary<string, Tuple<List<DataRecord>, StreamWriter>>();
|
||||
foreach (var record in records)
|
||||
{
|
||||
tableMapping.AddOrUpdate(record.TableName,
|
||||
Tuple.Create((List<DataRecord>) [record], new StreamWriter(File.OpenRead(record.TableName))),
|
||||
(_, tuple) =>
|
||||
{
|
||||
tuple.Item1.Add(record);
|
||||
return tuple;
|
||||
});
|
||||
}
|
||||
|
||||
var maxParallelism = 5;
|
||||
for (var i = 0; i < tableMapping.Count; i+=maxParallelism)
|
||||
{
|
||||
await Parallel.ForEachAsync(tableMapping.Take(maxParallelism), async (pair, token) =>
|
||||
{
|
||||
var (records, writer) = pair.Value;
|
||||
foreach (var record in records)
|
||||
{
|
||||
var content =
|
||||
$"""
|
||||
### {exception.Message}
|
||||
{record.RawField}
|
||||
""";
|
||||
await writer.WriteLineAsync(content);
|
||||
if (token.IsCancellationRequested)
|
||||
break;
|
||||
}
|
||||
await writer.DisposeAsync();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearErrorRecords(string dir)
|
||||
{
|
||||
Logger.LogInformation("***** Clear error records *****");
|
||||
foreach (var file in Directory.GetFiles(dir, "*.errlog", SearchOption.AllDirectories))
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
}
|
27
ConsoleApp2/Services/ErrorRecorder/ErrorRecorderFactory.cs
Normal file
27
ConsoleApp2/Services/ErrorRecorder/ErrorRecorderFactory.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ConsoleApp2.Services.ErrorRecorder;
|
||||
|
||||
public class ErrorRecorderFactory
|
||||
{
|
||||
private readonly ILogger<ErrorRecorderFactory> _logger;
|
||||
|
||||
public ErrorRecorderFactory(ILogger<ErrorRecorderFactory> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public OutputErrorRecorder CreateOutput(string database) => new(database, _logger);
|
||||
public TransformErrorRecorder CreateTransform() => new(_logger);
|
||||
public InputErrorRecorder CreateInput() => new(_logger);
|
||||
}
|
||||
|
||||
public static class ErrorRecorderFactoryExtensions
|
||||
{
|
||||
public static IServiceCollection AddErrorRecorderFactory(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<ErrorRecorderFactory>();
|
||||
return services;
|
||||
}
|
||||
}
|
19
ConsoleApp2/Services/ErrorRecorder/InputErrorRecorder.cs
Normal file
19
ConsoleApp2/Services/ErrorRecorder/InputErrorRecorder.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ConsoleApp2.Services.ErrorRecorder;
|
||||
|
||||
public sealed class InputErrorRecorder : ErrorRecorder
|
||||
{
|
||||
private readonly string _outputDir =
|
||||
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"ErrorRecords/{UID}/Input");
|
||||
|
||||
public InputErrorRecorder(ILogger logger) : base(logger)
|
||||
{
|
||||
}
|
||||
|
||||
public Task LogErrorRecordAsync(DataRecord record, Exception exception) =>
|
||||
LogErrorRecordAsync(_outputDir, record, exception);
|
||||
|
||||
public Task LogErrorRecordAsync(IEnumerable<DataRecord> records, Exception exception) =>
|
||||
LogErrorRecordAsync(_outputDir, records, exception);
|
||||
}
|
78
ConsoleApp2/Services/ErrorRecorder/OutputErrorRecorder.cs
Normal file
78
ConsoleApp2/Services/ErrorRecorder/OutputErrorRecorder.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ConsoleApp2.Services.ErrorRecorder;
|
||||
|
||||
|
||||
public sealed class OutputErrorRecorder : ErrorRecorder
|
||||
{
|
||||
private readonly string _outputDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"/ErrorRecords/{UID}/Output");
|
||||
private readonly string _database;
|
||||
private readonly Dictionary<string, int> _logIndex = new();
|
||||
|
||||
public OutputErrorRecorder(string database, ILogger logger) : base(logger)
|
||||
{
|
||||
_database = database;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录已知表名发生错误的SQL
|
||||
/// </summary>
|
||||
/// <param name="commandText"></param>
|
||||
/// <param name="tableName"></param>
|
||||
/// <param name="exception"></param>
|
||||
public async Task LogErrorSqlAsync(string commandText, string tableName, Exception exception)
|
||||
{
|
||||
if (!Directory.Exists(_outputDir))
|
||||
Directory.CreateDirectory(_outputDir);
|
||||
if (!_logIndex.TryGetValue(tableName, out var idx))
|
||||
{
|
||||
idx = 0;
|
||||
_logIndex.Add(tableName, idx);
|
||||
}
|
||||
var filePath = Path.Combine(_outputDir, $"{tableName}-{idx}.errlog");
|
||||
|
||||
if (File.Exists(filePath) && new FileInfo(filePath).Length > 10 * 1024 * 1024)
|
||||
{
|
||||
++idx;
|
||||
_logIndex[tableName] = idx;
|
||||
filePath = Path.Combine(_outputDir, $"{tableName}-{idx}.errlog");
|
||||
}
|
||||
var content = $"""
|
||||
/* [{DateTime.Now:yyyy-MM-dd HH:mm:ss}]
|
||||
* Error occurred when export table '{_database}.{tableName}':
|
||||
* {exception.Message}
|
||||
*/
|
||||
|
||||
USE `{_database}`;
|
||||
{commandText}
|
||||
|
||||
|
||||
""";
|
||||
await File.AppendAllTextAsync(filePath, content, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录发生错误的SQL
|
||||
/// </summary>
|
||||
/// <param name="commandText"></param>
|
||||
/// <param name="exception"></param>
|
||||
public async Task LogErrorSqlAsync(string commandText, Exception exception)
|
||||
{
|
||||
if (!Directory.Exists(_outputDir))
|
||||
Directory.CreateDirectory(_outputDir);
|
||||
var filePath = Path.Combine(_outputDir, "UnknownTables.errlog");
|
||||
var content = $"""
|
||||
/* [{DateTime.Now:yyyy-MM-dd HH:mm:ss}]
|
||||
* Error occurred when export table with unknown table name:
|
||||
* {exception.Message}
|
||||
*/
|
||||
{commandText}
|
||||
|
||||
|
||||
""";
|
||||
await File.AppendAllTextAsync(filePath, content, Encoding.UTF8);
|
||||
}
|
||||
|
||||
}
|
20
ConsoleApp2/Services/ErrorRecorder/TransformErrorRecorder.cs
Normal file
20
ConsoleApp2/Services/ErrorRecorder/TransformErrorRecorder.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ConsoleApp2.Services.ErrorRecorder;
|
||||
|
||||
public sealed class TransformErrorRecorder : ErrorRecorder
|
||||
{
|
||||
private readonly string _outputDir =
|
||||
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"ErrorRecords/{UID}/Transform");
|
||||
|
||||
|
||||
public TransformErrorRecorder(ILogger logger) : base(logger)
|
||||
{
|
||||
}
|
||||
|
||||
public Task LogErrorRecordAsync(DataRecord record, Exception exception) =>
|
||||
LogErrorRecordAsync(_outputDir, record, exception);
|
||||
|
||||
public Task LogErrorRecordAsync(IEnumerable<DataRecord> records, Exception exception) =>
|
||||
LogErrorRecordAsync(_outputDir, records, exception);
|
||||
}
|
Reference in New Issue
Block a user