You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET控制台应用如何添加文件日志并捕获日志用于程序结束后发邮件

实现方案

依赖安装

选用Serilog实现文件日志,先安装对应NuGet包:

  • Serilog.AspNetCore
  • Serilog.Sinks.File

核心实现步骤

步骤1:定义内存日志存储服务

用于存储所有运行时生成的日志条目,供后续发邮件使用:

// 日志条目实体
public class LogEntry
{
    public DateTimeOffset Timestamp { get; set; }
    public LogLevel Level { get; set; }
    public string Message { get; set; }
    public Exception? Exception { get; set; }
}

// 日志存储接口
public interface IInMemoryLogStore
{
    void Add(LogEntry entry);
    IReadOnlyList<LogEntry> GetAll();
}

// 日志存储实现
public class InMemoryLogStore : IInMemoryLogStore
{
    private readonly List<LogEntry> _logs = new();
    public void Add(LogEntry entry) => _logs.Add(entry);
    public IReadOnlyList<LogEntry> GetAll() => _logs.AsReadOnly();
}

步骤2:自定义日志Provider实现内存日志收集

public class InMemoryLoggerProvider : ILoggerProvider
{
    private readonly IInMemoryLogStore _logStore;
    public InMemoryLoggerProvider(IInMemoryLogStore logStore) => _logStore = logStore;

    public ILogger CreateLogger(string categoryName)
    {
        return new InMemoryLogger(_logStore, categoryName);
    }

    public void Dispose() { }
}

public class InMemoryLogger : ILogger
{
    private readonly IInMemoryLogStore _logStore;
    private readonly string _categoryName;

    public InMemoryLogger(IInMemoryLogStore logStore, string categoryName)
    {
        _logStore = logStore;
        _categoryName = categoryName;
    }

    public IDisposable BeginScope<TState>(TState state) => null;

    public bool IsEnabled(LogLevel logLevel) => true; // 按需调整过滤级别

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
    {
        if (!IsEnabled(logLevel)) return;
        var message = formatter(state, exception);
        _logStore.Add(new LogEntry
        {
            Timestamp = DateTimeOffset.Now,
            Level = logLevel,
            Message = $"[{_categoryName}] {message}",
            Exception = exception
        });
    }
}

步骤3:整合到Host配置中

var host = Host.CreateDefaultBuilder(args)
    // 注册内存日志存储为单例
    .ConfigureServices(services =>
    {
        services.AddSingleton<IInMemoryLogStore, InMemoryLogStore>();
    })
    .ConfigureLogging((context, logging) =>
    {
        logging.ClearProviders();
        logging.AddConsole();
        // 注册自定义内存日志Provider
        logging.Services.AddSingleton<ILoggerProvider, InMemoryLoggerProvider>();
    })
    // 配置Serilog实现文件日志输出
    .UseSerilog((context, config) =>
    {
        config
            .ReadFrom.Configuration(context.Configuration)
            .WriteTo.Console()
            .WriteTo.File(
                path: "logs/app-.log",
                rollingInterval: RollingInterval.Day,
                outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}");
    })
    .Build();

// 你的业务逻辑执行代码
// await host.RunAsync(); 或者自行解析服务执行业务

// 程序运行结束后,获取所有日志组装邮件
var logStore = host.Services.GetRequiredService<IInMemoryLogStore>();
var allLogs = logStore.GetAll();
// 此处实现邮件发送逻辑,把allLogs拼接为邮件内容即可

补充说明

  • 如果不想引入Serilog,也可以使用微软官方的Microsoft.Extensions.Logging.File包实现文件日志,仅需在ConfigureLogging中调用logging.AddFile()即可,不过Serilog的日志格式化、滚动策略配置更灵活,更适合生产环境使用。
  • 因为程序是单次运行的定时任务,内存存储日志不会有内存溢出风险,如果日志量极大,可以调整为临时写入本地文件,发邮件时再读取即可。

内容的提问来源于stack exchange,提问作者Jonathan Wood

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 08:36:03