C#中AWS.Logger.NLog发送CloudWatch失败时的文件回退问题
AWS CloudWatch日志回退逻辑不触发问题排查与解决
需求
- 向AWS CloudWatch发送日志失败时,自动将日志写入本地文件作为回退方案
问题
测试时输入错误配置值(比如无效密钥、不存在的区域),代码并未进入catch块,无法触发本地回退逻辑
使用依赖
AWS.Logger.NLog
相关代码
using Amazon.Runtime; using NLog.AWS.Logger; using NLog.Config; using NLog.Layouts; using NLog.Targets; using NLog; public class CloudWatchLogger { public CloudWatchLogger(CloudWatchLoggerReq req) { // Setup the NLog configuration var config = new LoggingConfiguration(); // Add console target config.AddRule(LogLevel.Trace, LogLevel.Fatal, new ConsoleTarget()); var awsTarget = new AWSTarget() { LogGroup = req.LogGroupName, // (Optional) How often batches of log messages should be pushed to AWS CloudWatch BatchPushInterval = TimeSpan.FromSeconds(3), FlushTimeout = TimeSpan.FromSeconds(req.TimeoutSec), // (Optional) Whether NLog should try create the log group if it does not exist or not DisableLogGroupCreation = true, // (Optional) The maximum number of log events that are kept in memory before being pushed to // AWS CloudWatch MaxQueuedMessages = 10000, // (Optional) A string to prefix log stream names with LogStreamNamePrefix = "Prefix", // (Optional) A string to suffix log stream names with LogStreamNameSuffix = "Suffix", // (Optional) AWS credentials that should be used to log into AWS and access AWS CloudWatch Credentials = new BasicAWSCredentials(req.Accesskey, req.SecretAccessKey), // CloudWatch logs should be logged in. In this case it defaults to the AWS Profile's region Region = req.Region, // (Optional) Custom layout that should be used when formatting log messages Layout = new SimpleLayout("${message}"), LogStreamName = "${date:format=yyyyMMdd}", // (Optional) Log System Error //LibraryLogFileName = $"{req.PathFileLog}/Logs/aws-logger-erros.txt", }; // Add AWS target and fallback file target config.AddRule(LogLevel.Trace, LogLevel.Fatal, awsTarget); // Add file target for fallback var fileTarget = new FileTarget("file") { FileName = $"{req.PathFileLog}/Logs/Log_${{date:format=yyyyMMdd}}.txt", Layout = new SimpleLayout("[${date}] ${message} ${exception}") }; config.AddRule(LogLevel.Error, LogLevel.Fatal, fileTarget); LogManager.Configuration = config; } //create logMessage public static void LogMessageAsync(SendMessageCloudWatch req) { var log = LogManager.GetCurrentClassLogger(); string msg = string.Empty; try { msg = req.msg; log.Trace(msg); } catch (Exception ex) { //Failed to send log to AWS CloudWatch string guid = System.Guid.NewGuid().ToString(); log.Error($"Req - {guid}: {msg}"); log.Error($"Res - {guid}: {ex}"); } } }
问题原因
- 异步批量推送机制:AWS.Logger.NLog的
AWSTarget是异步批量推送日志,调用log.Trace(msg)只是把日志加入内存队列,不会立即执行发送操作,因此不会抛出异常,自然进不了catch块。 - 回退规则不匹配:当前文件目标只捕获
Error及以上级别日志,但CloudWatch推送失败的错误属于目标内部错误,不会自动将原始Trace级别日志升级为Error,所以触发不了文件目标规则。
解决方案
方案1:启用AWSTarget内置错误日志
直接利用AWSTarget自带的错误日志配置,将推送失败的详情写入指定文件:
var awsTarget = new AWSTarget() { // 保留原有配置... // 开启库内部错误日志,记录CloudWatch推送失败信息 LibraryLogFileName = $"{req.PathFileLog}/Logs/aws-logger-errors.txt", // 可选:设置错误日志级别,默认是Error LibraryLogLevel = LogLevel.Error };
方案2:自定义错误事件处理
通过AWSTarget的Error事件捕获推送失败,手动将原始日志写入文件目标:
// 创建awsTarget后添加错误事件监听 awsTarget.Error += (sender, args) => { var fileLogger = LogManager.GetLogger("file"); // 对应文件目标的名称"file" // 将原始日志和错误信息一起写入本地文件 fileLogger.Error($"CloudWatch推送失败: {args.Exception.Message}\n原始日志内容: {args.LogEvent.Message}"); };
方案3:使用NLog的Fallback规则(NLog 4.7+支持)
修改规则配置,让日志先尝试发送到CloudWatch,失败则自动转发到文件目标:
// 替换原有的AddRule代码,添加fallback逻辑 config.AddRule(LogLevel.Trace, LogLevel.Fatal, awsTarget, "*", true) .FallbackRules.Add(new LoggingRule("*", LogLevel.Trace, LogLevel.Fatal, fileTarget));
测试注意事项
- 测试时可以将
BatchPushInterval设为1秒,加快错误触发速度 - 如果开启
DisableLogGroupCreation = false,需确保IAM账号有创建日志组的权限,否则会触发创建失败错误 - 异步推送的错误不会立即出现,需等待批量推送时间到后才会触发处理逻辑
内容的提问来源于stack exchange,提问作者shadowwave
相关产品推荐
相关产品推荐

