You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
10 KiB
286 lines
10 KiB
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 TaskManager.Controllers
|
|
{
|
|
public class LogConsumerService : BackgroundService
|
|
{
|
|
private readonly ChannelReader<TaskLog> _logReader;
|
|
|
|
private readonly ILogger<LogConsumerService> _logger;
|
|
private const int BatchSize = 100; // 批量写入大小
|
|
private readonly string _logDirectory;
|
|
private readonly IServiceProvider _serviceProvider;
|
|
|
|
|
|
public LogConsumerService(
|
|
LogController logService,
|
|
|
|
ILogger<LogConsumerService> logger, IServiceProvider serviceProvider)
|
|
{
|
|
_logReader = logService.GetLogReader();
|
|
|
|
_logger = logger;
|
|
_serviceProvider = serviceProvider; ;
|
|
_logDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CustomLogs"); // 使用更安全的路径获取方式
|
|
|
|
EnsureDirectoryExists(_logDirectory);
|
|
|
|
|
|
}
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
_logger.LogInformation("日志消费服务已启动");
|
|
|
|
// 批量处理日志,减少数据库写入次数
|
|
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); // 短暂延迟后重试
|
|
}
|
|
}
|
|
}
|
|
|
|
public override async Task StopAsync(CancellationToken stoppingToken)
|
|
{
|
|
_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数据
|
|
|
|
// 创建日期目录
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 后台服务
|
|
//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;
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|