|
|
@ -1,239 +1,286 @@ |
|
|
|
using Microsoft.AspNetCore.Mvc; |
|
|
|
using Microsoft.AspNetCore.Builder; |
|
|
|
using Microsoft.AspNetCore.Identity.Data; |
|
|
|
using Microsoft.AspNetCore.Mvc; |
|
|
|
using Microsoft.EntityFrameworkCore; |
|
|
|
using Microsoft.Extensions.Configuration; |
|
|
|
using Microsoft.Extensions.DependencyInjection; |
|
|
|
using Microsoft.Extensions.Hosting; |
|
|
|
using Microsoft.Extensions.Logging; |
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Linq; |
|
|
|
using System.Net.Http.Json; |
|
|
|
using System.Text; |
|
|
|
using System.Text.Encodings.Web; |
|
|
|
using System.Text.Json; |
|
|
|
using System.Threading.Channels; |
|
|
|
using System.Threading.Tasks; |
|
|
|
using TaskManager.Entity; |
|
|
|
using TaskManager.EntityFramework; |
|
|
|
using Wood.Entity; |
|
|
|
using Wood.Service.SystemManage; |
|
|
|
|
|
|
|
namespace Wood.Service.Controllers |
|
|
|
namespace TaskManager.Controllers |
|
|
|
{ |
|
|
|
public class LogController1 : BackgroundService |
|
|
|
public class LogConsumerService : BackgroundService |
|
|
|
{ |
|
|
|
private readonly IServiceProvider _serviceProvider; |
|
|
|
private readonly Channel<TaskLog> _logChannel; |
|
|
|
private readonly ChannelReader<TaskLog> _logReader; |
|
|
|
|
|
|
|
private readonly ILogger<LogConsumerService> _logger; |
|
|
|
private const int BatchSize = 100; // 批量写入大小
|
|
|
|
private readonly string _logDirectory; |
|
|
|
private readonly IServiceProvider _serviceProvider; |
|
|
|
|
|
|
|
public LogController1( |
|
|
|
IServiceProvider serviceProvider, |
|
|
|
IConfiguration configuration) |
|
|
|
|
|
|
|
public LogConsumerService( |
|
|
|
LogController logService, |
|
|
|
|
|
|
|
ILogger<LogConsumerService> logger, IServiceProvider serviceProvider) |
|
|
|
{ |
|
|
|
_serviceProvider = serviceProvider; |
|
|
|
_logDirectory = configuration["Logging:Directory"] ?? |
|
|
|
Path.Combine(Directory.GetCurrentDirectory(), "Logs"); |
|
|
|
_logReader = logService.GetLogReader(); |
|
|
|
|
|
|
|
_logger = logger; |
|
|
|
_serviceProvider = serviceProvider; ; |
|
|
|
_logDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CustomLogs"); // 使用更安全的路径获取方式
|
|
|
|
|
|
|
|
EnsureDirectoryExists(_logDirectory); |
|
|
|
_logChannel = Channel.CreateUnbounded<TaskLog>(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public void EnqueueLog(TaskLog logMessage) |
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken) |
|
|
|
{ |
|
|
|
// 设置日志路径并写入文件
|
|
|
|
logMessage.Path= WriteLogToFile(logMessage); |
|
|
|
_logger.LogInformation("日志消费服务已启动"); |
|
|
|
|
|
|
|
// 将日志加入处理队列
|
|
|
|
_logChannel.Writer.TryWrite(logMessage); |
|
|
|
// 批量处理日志,减少数据库写入次数
|
|
|
|
while (!stoppingToken.IsCancellationRequested && |
|
|
|
await _logReader.WaitToReadAsync(stoppingToken)) |
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
var logs = new List<TaskLog>(); |
|
|
|
int count = 0; |
|
|
|
|
|
|
|
// 读取一批日志
|
|
|
|
while (_logReader.TryRead(out var log) && count < BatchSize) |
|
|
|
{ |
|
|
|
logs.Add(log); |
|
|
|
count++; |
|
|
|
} |
|
|
|
|
|
|
|
if (logs.Any()) |
|
|
|
{ |
|
|
|
|
|
|
|
using var scope = _serviceProvider.CreateScope(); |
|
|
|
var db = scope.ServiceProvider.GetRequiredService<JobDbContext>(); |
|
|
|
|
|
|
|
List<TaskLog> logsToSave = new List<TaskLog>(); |
|
|
|
foreach (var log in logs) |
|
|
|
{ |
|
|
|
if (!string.IsNullOrEmpty(log.Remark)) |
|
|
|
{ |
|
|
|
log.Path = WriteLogToFile(log.Remark); |
|
|
|
} |
|
|
|
log.Remark = string.Empty; |
|
|
|
logsToSave.Add(log); |
|
|
|
} |
|
|
|
|
|
|
|
await db.TaskLogs.AddRangeAsync(logsToSave, stoppingToken); |
|
|
|
await db.SaveChangesAsync(stoppingToken); |
|
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation($"已写入 {logs.Count} 条日志"); |
|
|
|
} |
|
|
|
} |
|
|
|
catch (Exception ex) |
|
|
|
{ |
|
|
|
_logger.LogError(ex, "日志写入数据库失败"); |
|
|
|
// 错误处理:可记录到临时文件或重试
|
|
|
|
await Task.Delay(1000, stoppingToken); // 短暂延迟后重试
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private string WriteLogToFile(TaskLog logMessage) |
|
|
|
public override async Task StopAsync(CancellationToken stoppingToken) |
|
|
|
{ |
|
|
|
// 获取今天的日期目录
|
|
|
|
string todayDirectory = Path.Combine(_logDirectory, DateTime.Now.ToString("yyyy-MM-dd")); |
|
|
|
_logger.LogInformation("日志消费服务正在停止"); |
|
|
|
// 处理剩余日志
|
|
|
|
await base.StopAsync(stoppingToken); |
|
|
|
} |
|
|
|
private void EnsureDirectoryExists(string directoryPath) |
|
|
|
{ |
|
|
|
if (!Directory.Exists(directoryPath)) |
|
|
|
{ |
|
|
|
Directory.CreateDirectory(directoryPath); |
|
|
|
} |
|
|
|
} |
|
|
|
// 修改后的日志写入方法
|
|
|
|
private string WriteLogToFile(string jsonContent) |
|
|
|
{ |
|
|
|
//if string.IsNullOrEmpty(logMessage.RawRemark)) return null; // 必须提供JSON数据
|
|
|
|
|
|
|
|
// 确保目录存在
|
|
|
|
EnsureDirectoryExists(todayDirectory); |
|
|
|
// 创建日期目录
|
|
|
|
string dateDirectory = DateTime.Now.ToString("yyyy-MM-dd"); |
|
|
|
string fullDatePath = Path.Combine(_logDirectory, dateDirectory); |
|
|
|
EnsureDirectoryExists(fullDatePath); |
|
|
|
|
|
|
|
// 创建或追加到日志文件
|
|
|
|
string logFilePath = Path.Combine(todayDirectory, "application.log"); |
|
|
|
// 生成唯一文件名(时间戳+随机数)
|
|
|
|
string fileName = $"log_{DateTime.Now.Ticks}_{Random.Shared.Next(1000, 9999)}.json"; |
|
|
|
string fullPath = Path.Combine(fullDatePath, fileName); |
|
|
|
|
|
|
|
try |
|
|
|
{ |
|
|
|
// 写入日志内容到文件
|
|
|
|
File.AppendAllText(logFilePath, |
|
|
|
$"[{logMessage.CreationTime:yyyy-MM-dd HH:mm:ss}] [{logMessage.Type}] {logMessage.Info}{Environment.NewLine}"); |
|
|
|
|
|
|
|
// 返回相对路径(从日志根目录开始)
|
|
|
|
return Path.GetRelativePath(_logDirectory, logFilePath); |
|
|
|
// 写入文件(使用UTF-8无BOM格式)
|
|
|
|
File.WriteAllText(fullPath, jsonContent, new UTF8Encoding(false)); |
|
|
|
|
|
|
|
// 存储相对路径(从日志根目录开始,使用正斜杠兼容API)
|
|
|
|
return Path.Combine(dateDirectory, fileName).Replace('\\', '/'); |
|
|
|
} |
|
|
|
catch (Exception ex) |
|
|
|
{ |
|
|
|
// 记录日志写入失败的错误
|
|
|
|
Console.WriteLine($"Error writing log to file: {ex.Message}"); |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
Console.WriteLine($"JSON文件写入失败:{ex.Message}"); |
|
|
|
|
|
|
|
private void EnsureDirectoryExists(string directoryPath) |
|
|
|
{ |
|
|
|
if (!Directory.Exists(directoryPath)) |
|
|
|
{ |
|
|
|
Directory.CreateDirectory(directoryPath); |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//[HttpGet("AddError")]
|
|
|
|
//public async Task<bool> AddError(string message, string taskname, Guid taskid, Version version)
|
|
|
|
//{
|
|
|
|
// var log = new TaskLog
|
|
|
|
// {
|
|
|
|
// Info = message,
|
|
|
|
// Type = "错误",
|
|
|
|
// TaskName = taskname,
|
|
|
|
// CreationTime = DateTime.Now,
|
|
|
|
// TaskId = taskid,
|
|
|
|
// Version = version?.ToString()
|
|
|
|
// };
|
|
|
|
|
|
|
|
// EnqueueLog(log);
|
|
|
|
|
|
|
|
// return true; // 日志已入队,视为成功
|
|
|
|
//}
|
|
|
|
|
|
|
|
//[HttpGet("AddInfo")]
|
|
|
|
//public async Task<bool> AddInfo(string message, string taskname, Guid taskid, Version version)
|
|
|
|
//{
|
|
|
|
// var log = new TaskLog
|
|
|
|
// {
|
|
|
|
// Info = message,
|
|
|
|
// Type = "错误",
|
|
|
|
// TaskName = taskname,
|
|
|
|
// CreationTime = DateTime.Now,
|
|
|
|
// TaskId = taskid,
|
|
|
|
// Version = version?.ToString()
|
|
|
|
// };
|
|
|
|
|
|
|
|
// EnqueueLog(log);
|
|
|
|
|
|
|
|
// return true;
|
|
|
|
//}
|
|
|
|
|
|
|
|
//[HttpGet("AddInfoRemark")]
|
|
|
|
//public async Task<bool> AddInfoRemark(string message, string taskname, string remark)
|
|
|
|
//{
|
|
|
|
// var log = new TaskLog
|
|
|
|
// {
|
|
|
|
// Info = message,
|
|
|
|
// Type = "记录",
|
|
|
|
// TaskName = taskname,
|
|
|
|
// CreationTime = DateTime.Now,
|
|
|
|
// Remark = remark
|
|
|
|
// };
|
|
|
|
|
|
|
|
// EnqueueLog(log);
|
|
|
|
|
|
|
|
// return true;
|
|
|
|
//}
|
|
|
|
|
|
|
|
[HttpGet("AddError")] |
|
|
|
public async Task<bool> AddError(string message, string taskname, Guid taskid, string version) |
|
|
|
{ |
|
|
|
var log = new TaskLog |
|
|
|
{ |
|
|
|
Info = message, |
|
|
|
Type = "错误", |
|
|
|
TaskName = taskname, |
|
|
|
CreationTime = DateTime.Now, |
|
|
|
TaskId = taskid, |
|
|
|
Version = version?.ToString() |
|
|
|
}; |
|
|
|
|
|
|
|
EnqueueLog(log); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; // 日志已入队,视为成功
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
[HttpGet("AddInfo")] |
|
|
|
public async Task<bool> AddInfo(string message, string taskname, Guid taskid, string version) |
|
|
|
{ |
|
|
|
var log = new TaskLog |
|
|
|
{ |
|
|
|
Info = message, |
|
|
|
Type = "信息", |
|
|
|
TaskName = taskname, |
|
|
|
CreationTime = DateTime.Now, |
|
|
|
TaskId = taskid, |
|
|
|
Version = version?.ToString() |
|
|
|
}; |
|
|
|
|
|
|
|
EnqueueLog(log); |
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
[HttpGet("AddPostRequest")] |
|
|
|
public async Task<bool> AddPostRequest(string message, string taskname, Guid taskid, string version, string remark) |
|
|
|
{ |
|
|
|
var log = new TaskLog |
|
|
|
{ |
|
|
|
Info = message, |
|
|
|
Type = "请求", |
|
|
|
TaskName = taskname, |
|
|
|
CreationTime = DateTime.Now, |
|
|
|
TaskId = taskid, |
|
|
|
Version = version?.ToString(), |
|
|
|
Remark = remark |
|
|
|
}; |
|
|
|
EnqueueLog(log); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
[HttpGet("AddPostResponse")] |
|
|
|
public async Task<bool> AddPostResponse(string message, string taskname, Guid taskid, string version, string remaek) |
|
|
|
{ |
|
|
|
var log = new TaskLog |
|
|
|
{ |
|
|
|
Info = message, |
|
|
|
Type = "应答", |
|
|
|
TaskName = taskname, |
|
|
|
CreationTime = DateTime.Now, |
|
|
|
TaskId = taskid, |
|
|
|
Version = version?.ToString(), |
|
|
|
Remark = remaek |
|
|
|
}; |
|
|
|
EnqueueLog(log); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 后台服务
|
|
|
|
//public class LogBackgroundService : BackgroundService
|
|
|
|
//{
|
|
|
|
|
|
|
|
// private readonly Channel<TaskLog> _logChannel;
|
|
|
|
// private readonly IServiceProvider _serviceProvider;
|
|
|
|
// private readonly string _logDirectory;
|
|
|
|
|
|
|
|
// public LogBackgroundService(IServiceProvider serviceProvider, IConfiguration configuration)
|
|
|
|
// {
|
|
|
|
// Console.WriteLine("LogService 初始化");
|
|
|
|
// _serviceProvider = serviceProvider;
|
|
|
|
// _logDirectory = configuration["Logging:Directory"] ??
|
|
|
|
// Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CustomLogs"); // 使用更安全的路径获取方式
|
|
|
|
|
|
|
|
// EnsureDirectoryExists(_logDirectory);
|
|
|
|
// _logChannel = Channel.CreateUnbounded<TaskLog>();
|
|
|
|
// }
|
|
|
|
|
|
|
|
// public void EnqueueLog(TaskLog log)
|
|
|
|
// {
|
|
|
|
// _logChannel.Writer.TryWrite(log);
|
|
|
|
// }
|
|
|
|
// public override async Task StartAsync(CancellationToken cancellationToken)
|
|
|
|
// {
|
|
|
|
// // 服务启动前的准备工作
|
|
|
|
// // _logger.LogInformation("Worker starting up...");
|
|
|
|
|
|
|
|
// // 调用基类方法
|
|
|
|
// await base.StartAsync(cancellationToken);
|
|
|
|
// }
|
|
|
|
|
|
|
|
// public override async Task StopAsync(CancellationToken cancellationToken)
|
|
|
|
// {
|
|
|
|
// // 服务停止前的清理工作
|
|
|
|
// //_logger.LogInformation("Worker shutting down...");
|
|
|
|
|
|
|
|
// // 调用基类方法
|
|
|
|
// await base.StopAsync(cancellationToken);
|
|
|
|
// }
|
|
|
|
|
|
|
|
// public override void Dispose()
|
|
|
|
// {
|
|
|
|
// // 资源释放
|
|
|
|
// // _logger.LogInformation("Worker disposing resources");
|
|
|
|
// base.Dispose();
|
|
|
|
// }
|
|
|
|
// protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
|
|
// {
|
|
|
|
// Console.WriteLine("LogService 开始执行后台任务");
|
|
|
|
|
|
|
|
|
|
|
|
// using PeriodicTimer timer = new(TimeSpan.FromSeconds(10));
|
|
|
|
|
|
|
|
|
|
|
|
// // 主循环 - 使用 PeriodicTimer 等待下一个触发时间
|
|
|
|
// while (await timer.WaitForNextTickAsync(stoppingToken))
|
|
|
|
// {
|
|
|
|
// await foreach (var log in _logChannel.Reader.ReadAllAsync(stoppingToken))
|
|
|
|
// {
|
|
|
|
// try
|
|
|
|
// {
|
|
|
|
// if (!string.IsNullOrEmpty(log.Remark))
|
|
|
|
// {
|
|
|
|
// log.Path = WriteLogToFile(log.Remark);
|
|
|
|
// }
|
|
|
|
// log.Remark = string.Empty;
|
|
|
|
// using var scope = _serviceProvider.CreateScope();
|
|
|
|
// var db = scope.ServiceProvider.GetRequiredService<JobDbContext>();
|
|
|
|
// await db.TaskLogs.AddAsync(log, stoppingToken);
|
|
|
|
// await db.SaveChangesAsync(stoppingToken);
|
|
|
|
// Console.WriteLine($"日志已保存: {log.Info}");
|
|
|
|
// }
|
|
|
|
// catch (Exception ex)
|
|
|
|
// {
|
|
|
|
// Console.WriteLine($"日志处理失败: {ex.Message}");
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// Console.WriteLine("LogService 后台任务已停止");
|
|
|
|
// }
|
|
|
|
// private void EnsureDirectoryExists(string directoryPath)
|
|
|
|
// {
|
|
|
|
// if (!Directory.Exists(directoryPath))
|
|
|
|
// {
|
|
|
|
// Directory.CreateDirectory(directoryPath);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// // 修改后的日志写入方法
|
|
|
|
// private string WriteLogToFile(string jsonContent)
|
|
|
|
// {
|
|
|
|
// //if string.IsNullOrEmpty(logMessage.RawRemark)) return null; // 必须提供JSON数据
|
|
|
|
|
|
|
|
// // 创建日期目录
|
|
|
|
// string dateDirectory = DateTime.Now.ToString("yyyy-MM-dd");
|
|
|
|
// string fullDatePath = Path.Combine(_logDirectory, dateDirectory);
|
|
|
|
// EnsureDirectoryExists(fullDatePath);
|
|
|
|
|
|
|
|
// // 生成唯一文件名(时间戳+随机数)
|
|
|
|
// string fileName = $"log_{DateTime.Now.Ticks}_{Random.Shared.Next(1000, 9999)}.json";
|
|
|
|
// string fullPath = Path.Combine(fullDatePath, fileName);
|
|
|
|
|
|
|
|
// try
|
|
|
|
// {
|
|
|
|
|
|
|
|
// // 写入文件(使用UTF-8无BOM格式)
|
|
|
|
// File.WriteAllText(fullPath, jsonContent, new UTF8Encoding(false));
|
|
|
|
|
|
|
|
// // 存储相对路径(从日志根目录开始,使用正斜杠兼容API)
|
|
|
|
// return Path.Combine(dateDirectory, fileName).Replace('\\', '/');
|
|
|
|
// }
|
|
|
|
// catch (Exception ex)
|
|
|
|
// {
|
|
|
|
// Console.WriteLine($"JSON文件写入失败:{ex.Message}");
|
|
|
|
|
|
|
|
// return null;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 其他方法保持不变...
|
|
|
|
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken) |
|
|
|
{ |
|
|
|
await foreach (var logMessage in _logChannel.Reader.ReadAllAsync(stoppingToken)) |
|
|
|
{ |
|
|
|
// 使用IServiceScopeFactory创建独立的作用域
|
|
|
|
using var scope = _serviceProvider.CreateScope(); |
|
|
|
var dbContext = scope.ServiceProvider.GetRequiredService<JobDbContext>(); |
|
|
|
|
|
|
|
try |
|
|
|
{ |
|
|
|
dbContext.TaskLogs.Add(logMessage); |
|
|
|
await dbContext.SaveChangesAsync(stoppingToken); |
|
|
|
} |
|
|
|
catch (Exception ex) |
|
|
|
{ |
|
|
|
Console.WriteLine($"Error saving log: {ex.Message}"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|