活动公告

系统通知
05-18 21:22
系统通知
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,资源失效请在帖子内回复要求补档,会尽快处理!
10-23 09:31

C#输出窗口信息显示完全指南从Console WriteLine到Debug调试技巧详解

SunJu_FaceMall

3万

主题

2860

科技点

3万

积分

白金月票

碾压王

积分
32872

塔罗立华奏

<font color=白金月票" /> 发表于 2025-9-9 18:00:12 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
在C#开发过程中,输出信息显示是开发者与程序交互的重要方式。无论是简单的控制台应用程序还是复杂的企业级系统,有效地输出和显示信息都是调试、监控和维护程序的关键环节。本文将全面介绍C#中从基础的Console.WriteLine到高级的Debug调试技巧,帮助开发者掌握各种输出窗口信息显示的方法,提高开发效率和程序调试能力。

基础输出方法:Console.WriteLine及其相关方法

Console.WriteLine基础用法

Console.WriteLine是C#中最基本也是最常用的输出方法,它将指定的文本或数据输出到控制台窗口,并在末尾添加一个换行符。
  1. // 输出简单文本
  2. Console.WriteLine("Hello, World!");
  3. // 输出变量的值
  4. int number = 42;
  5. Console.WriteLine(number);
  6. // 使用格式化字符串输出
  7. string name = "Alice";
  8. int age = 30;
  9. Console.WriteLine("Name: {0}, Age: {1}", name, age);
  10. // 使用字符串插值(C# 6.0及以上版本)
  11. Console.WriteLine($"Name: {name}, Age: {age}");
复制代码

Console.Write方法

与Console.WriteLine不同,Console.Write方法在输出后不添加换行符。
  1. Console.Write("This is on the same line. ");
  2. Console.Write("This continues the same line.");
  3. // 输出: This is on the same line. This continues the same line.
复制代码

格式化输出

C#提供了多种格式化输出的方式,使开发者能够控制数据的显示格式。
  1. // 数值格式化
  2. double price = 19.99;
  3. Console.WriteLine("Price: {0:C}", price); // 货币格式
  4. Console.WriteLine("Price: {0:F2}", price); // 固定小数点格式
  5. Console.WriteLine("Percentage: {0:P}", 0.25); // 百分比格式
  6. // 日期格式化
  7. DateTime now = DateTime.Now;
  8. Console.WriteLine("Date: {0:d}", now); // 短日期格式
  9. Console.WriteLine("Date: {0:D}", now); // 长日期格式
  10. Console.WriteLine("Time: {0:t}", now); // 短时间格式
  11. Console.WriteLine("Time: {0:T}", now); // 长时间格式
复制代码

控制台颜色设置

开发者可以通过设置控制台的前景色和背景色来增强输出的可读性。
  1. // 保存当前颜色
  2. ConsoleColor originalForegroundColor = Console.ForegroundColor;
  3. ConsoleColor originalBackgroundColor = Console.BackgroundColor;
  4. // 设置新的颜色
  5. Console.ForegroundColor = ConsoleColor.Yellow;
  6. Console.BackgroundColor = ConsoleColor.Blue;
  7. // 输出彩色文本
  8. Console.WriteLine("This is a colored message.");
  9. // 恢复原始颜色
  10. Console.ForegroundColor = originalForegroundColor;
  11. Console.BackgroundColor = originalBackgroundColor;
复制代码

控制台窗口操作

C#还提供了操作控制台窗口的方法,如设置窗口大小、标题等。
  1. // 设置窗口标题
  2. Console.Title = "My Console Application";
  3. // 设置窗口大小
  4. Console.WindowWidth = 100;
  5. Console.WindowHeight = 40;
  6. // 清除控制台窗口
  7. Console.Clear();
  8. // 获取光标位置并设置
  9. int left = Console.CursorLeft;
  10. int top = Console.CursorTop;
  11. Console.SetCursorPosition(10, 5);
  12. Console.WriteLine("Positioned text");
  13. Console.SetCursorPosition(left, top);
复制代码

Console.ReadLine和Console.ReadKey

除了输出方法,Console类还提供了读取用户输入的方法。
  1. // 读取一行文本
  2. Console.WriteLine("Please enter your name:");
  3. string name = Console.ReadLine();
  4. Console.WriteLine($"Hello, {name}!");
  5. // 读取单个按键
  6. Console.WriteLine("Press any key to continue...");
  7. ConsoleKeyInfo keyInfo = Console.ReadKey();
  8. Console.WriteLine($"\nYou pressed: {keyInfo.Key}");
复制代码

调试输出:Debug类和Trace类的使用

Debug类基础用法

System.Diagnostics.Debug类提供了一组方法,用于在调试过程中输出信息。这些输出只在调试模式下可见,在发布版本中会被自动忽略。
  1. using System.Diagnostics;
  2. // 基本Debug输出
  3. Debug.WriteLine("This is a debug message");
  4. // 条件Debug输出
  5. bool debugMode = true;
  6. Debug.WriteLineIf(debugMode, "Debug mode is active");
  7. // 断言
  8. int value = 5;
  9. Debug.Assert(value > 10, "Value should be greater than 10");
复制代码

Trace类基础用法

System.Diagnostics.Trace类与Debug类类似,但它的输出在调试和发布版本中都会保留。
  1. using System.Diagnostics;
  2. // 基本Trace输出
  3. Trace.WriteLine("This is a trace message");
  4. // 条件Trace输出
  5. bool traceEnabled = true;
  6. Trace.WriteLineIf(traceEnabled, "Trace is enabled");
  7. // 警告和错误信息
  8. Trace.TraceWarning("This is a warning");
  9. Trace.TraceError("This is an error");
复制代码

Debug和Trace的配置

开发者可以通过配置文件或代码来配置Debug和Trace的输出目标。
  1. using System.Diagnostics;
  2. // 创建文本写入器作为Trace监听器
  3. TextWriterTraceListener textListener = new TextWriterTraceListener("Output.log");
  4. Trace.Listeners.Add(textListener);
  5. // 创建控制台监听器
  6. ConsoleTraceListener consoleListener = new ConsoleTraceListener();
  7. Trace.Listeners.Add(consoleListener);
  8. // 自动刷新输出
  9. Trace.AutoFlush = true;
  10. // 使用Trace输出
  11. Trace.WriteLine("This message will appear in both console and log file");
  12. // 关闭监听器
  13. Trace.Close();
复制代码

在应用程序配置文件(app.config或web.config)中,可以配置Trace监听器:
  1. <configuration>
  2.   <system.diagnostics>
  3.     <trace autoflush="true">
  4.       <listeners>
  5.         <add name="consoleListener" type="System.Diagnostics.ConsoleTraceListener" />
  6.         <add name="textListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="Output.log" />
  7.         <add name="eventLogListener" type="System.Diagnostics.EventLogTraceListener" initializeData="MyApplication" />
  8.       </listeners>
  9.     </trace>
  10.   </system.diagnostics>
  11. </configuration>
复制代码

Debug和Trace的输出级别

可以通过设置TraceLevel来控制输出的详细程度。
  1. using System.Diagnostics;
  2. // 设置Trace级别
  3. TraceSwitch traceSwitch = new TraceSwitch("General", "Entire Application");
  4. traceSwitch.Level = TraceLevel.Verbose;
  5. // 根据级别输出信息
  6. Trace.WriteLineIf(traceSwitch.TraceError, "Error message");
  7. Trace.WriteLineIf(traceSwitch.TraceWarning, "Warning message");
  8. Trace.WriteLineIf(traceSwitch.TraceInfo, "Info message");
  9. Trace.WriteLineIf(traceSwitch.TraceVerbose, "Verbose message");
复制代码

使用Debug和Trace进行性能分析

Debug和Trace类还可以用于简单的性能分析。
  1. using System.Diagnostics;
  2. // 开始计时
  3. Debug.WriteLine("Starting operation");
  4. Stopwatch stopwatch = Stopwatch.StartNew();
  5. // 执行一些操作
  6. Thread.Sleep(1000);
  7. // 停止计时并输出结果
  8. stopwatch.Stop();
  9. Debug.WriteLine($"Operation completed in {stopwatch.ElapsedMilliseconds} ms");
复制代码

高级调试技巧:条件输出、格式化输出等

条件编译符号

使用条件编译符号,可以控制代码是否被编译到程序中。
  1. #define DEBUG
  2. #define TRACE
  3. using System.Diagnostics;
  4. public class Program
  5. {
  6.     public static void Main()
  7.     {
  8. #if DEBUG
  9.         Console.WriteLine("Debug version");
  10. #endif
  11. #if TRACE
  12.         Console.WriteLine("Trace enabled");
  13. #endif
  14. #if DEBUG && TRACE
  15.         Console.WriteLine("Both DEBUG and TRACE are defined");
  16. #endif
  17.     }
  18. }
复制代码

Conditional特性

Conditional特性允许根据条件编译符号来决定是否调用方法。
  1. using System.Diagnostics;
  2. public class Logger
  3. {
  4.     [Conditional("DEBUG")]
  5.     public static void LogDebug(string message)
  6.     {
  7.         Console.WriteLine($"DEBUG: {message}");
  8.     }
  9.     [Conditional("TRACE")]
  10.     public static void LogTrace(string message)
  11.     {
  12.         Console.WriteLine($"TRACE: {message}");
  13.     }
  14. }
  15. public class Program
  16. {
  17.     public static void Main()
  18.     {
  19.         Logger.LogDebug("This is a debug message");
  20.         Logger.LogTrace("This is a trace message");
  21.     }
  22. }
复制代码

Debugger类的高级用法

System.Diagnostics.Debugger类提供了与调试器交互的高级方法。
  1. using System.Diagnostics;
  2. public class Program
  3. {
  4.     public static void Main()
  5.     {
  6.         // 检查是否附加了调试器
  7.         if (Debugger.IsAttached)
  8.         {
  9.             Console.WriteLine("Debugger is attached");
  10.         }
  11.         // 启动调试器并附加到当前进程
  12.         if (!Debugger.IsAttached)
  13.         {
  14.             Debugger.Launch();
  15.         }
  16.         // 设置断点(仅在调试器中有效)
  17.         Debugger.Break();
  18.         // 输出调试信息
  19.         Debug.WriteLine("This is a debug message");
  20.     }
  21. }
复制代码

使用DebuggerDisplay特性

DebuggerDisplay特性可以自定义对象在调试器中的显示方式。
  1. using System.Diagnostics;
  2. [DebuggerDisplay("Name = {Name}, Age = {Age}")]
  3. public class Person
  4. {
  5.     public string Name { get; set; }
  6.     public int Age { get; set; }
  7. }
  8. public class Program
  9. {
  10.     public static void Main()
  11.     {
  12.         var person = new Person { Name = "Alice", Age = 30 };
  13.         // 在调试器中,person对象将显示为 "Name = Alice, Age = 30"
  14.         Console.WriteLine(person.Name);
  15.     }
  16. }
复制代码

使用DebuggerStepThrough和DebuggerHidden特性

这些特性可以控制调试器如何处理特定方法。
  1. using System.Diagnostics;
  2. public class MathUtils
  3. {
  4.     [DebuggerStepThrough]
  5.     public static int Add(int a, int b)
  6.     {
  7.         // 调试器将单步执行此方法,而不是进入方法内部
  8.         return a + b;
  9.     }
  10.     [DebuggerHidden]
  11.     public static int Multiply(int a, int b)
  12.     {
  13.         // 调试器将完全隐藏此方法
  14.         return a * b;
  15.     }
  16. }
复制代码

使用TraceSource进行结构化跟踪

TraceSource类提供了更结构化的跟踪方式,允许定义不同的跟踪源和级别。
  1. using System.Diagnostics;
  2. public class Program
  3. {
  4.     private static TraceSource traceSource = new TraceSource("MyApp");
  5.     public static void Main()
  6.     {
  7.         // 配置TraceSource
  8.         traceSource.Switch = new SourceSwitch("SourceSwitch", "All");
  9.         traceSource.Listeners.Add(new ConsoleTraceListener());
  10.         // 输出不同级别的跟踪信息
  11.         traceSource.TraceEvent(TraceEventType.Error, 0, "Error message");
  12.         traceSource.TraceEvent(TraceEventType.Warning, 0, "Warning message");
  13.         traceSource.TraceEvent(TraceEventType.Information, 0, "Information message");
  14.         traceSource.TraceEvent(TraceEventType.Verbose, 0, "Verbose message");
  15.         // 关闭TraceSource
  16.         traceSource.Close();
  17.     }
  18. }
复制代码

使用CorrelationManager跟踪操作

CorrelationManager类可以帮助跟踪跨线程和跨活动的操作。
  1. using System.Diagnostics;
  2. public class Program
  3. {
  4.     public static void Main()
  5.     {
  6.         Trace.CorrelationManager.ActivityId = Guid.NewGuid();
  7.         Trace.CorrelationManager.StartLogicalOperation("MainOperation");
  8.         Trace.WriteLine("Starting main operation");
  9.         // 模拟子操作
  10.         Trace.CorrelationManager.StartLogicalOperation("SubOperation");
  11.         Trace.WriteLine("Starting sub operation");
  12.         Trace.CorrelationManager.StopLogicalOperation();
  13.         Trace.WriteLine("Sub operation completed");
  14.         Trace.CorrelationManager.StopLogicalOperation();
  15.         Trace.WriteLine("Main operation completed");
  16.     }
  17. }
复制代码

日志记录框架:NLog、log4net、Serilog等

NLog使用指南

NLog是一个灵活且免费的日志记录平台,适用于各种.NET平台。

首先,通过NuGet安装NLog包:
  1. Install-Package NLog
复制代码

可以通过代码或配置文件来配置NLog。
  1. using NLog;
  2. using NLog.Targets;
  3. using NLog.Config;
  4. public class Program
  5. {
  6.     public static void Main()
  7.     {
  8.         // 创建日志配置
  9.         var config = new LoggingConfiguration();
  10.         // 创建控制台目标
  11.         var consoleTarget = new ColoredConsoleTarget("console")
  12.         {
  13.             Layout = "${longdate}|${level:uppercase=true}|${logger}|${message}"
  14.         };
  15.         // 创建文件目标
  16.         var fileTarget = new FileTarget("file")
  17.         {
  18.             FileName = "${basedir}/logs/${shortdate}.log",
  19.             Layout = "${longdate}|${level:uppercase=true}|${logger}|${message}"
  20.         };
  21.         // 添加目标到配置
  22.         config.AddTarget(consoleTarget);
  23.         config.AddTarget(fileTarget);
  24.         // 创建规则
  25.         config.AddRule(LogLevel.Debug, LogLevel.Fatal, consoleTarget);
  26.         config.AddRule(LogLevel.Debug, LogLevel.Fatal, fileTarget);
  27.         // 应用配置
  28.         LogManager.Configuration = config;
  29.         // 获取日志记录器并记录日志
  30.         var logger = LogManager.GetCurrentClassLogger();
  31.         logger.Debug("This is a debug message");
  32.         logger.Info("This is an info message");
  33.         logger.Warn("This is a warning message");
  34.         logger.Error("This is an error message");
  35.         logger.Fatal("This is a fatal error message");
  36.     }
  37. }
复制代码

在项目中添加NLog.config文件:
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
  3.       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.       autoReload="true"
  5.       throwExceptions="false">
  6.   <targets>
  7.     <target name="console" xsi:type="ColoredConsole"
  8.             layout="${longdate}|${level:uppercase=true}|${logger}|${message}" />
  9.    
  10.     <target name="file" xsi:type="File"
  11.             fileName="${basedir}/logs/${shortdate}.log"
  12.             layout="${longdate}|${level:uppercase=true}|${logger}|${message}" />
  13.   </targets>
  14.   
  15.   <rules>
  16.     <logger name="*" minlevel="Debug" writeTo="console" />
  17.     <logger name="*" minlevel="Debug" writeTo="file" />
  18.   </rules>
  19. </nlog>
复制代码

然后在代码中使用:
  1. using NLog;
  2. public class Program
  3. {
  4.     private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
  5.     public static void Main()
  6.     {
  7.         Logger.Debug("This is a debug message");
  8.         Logger.Info("This is an info message");
  9.         Logger.Warn("This is a warning message");
  10.         Logger.Error("This is an error message");
  11.         Logger.Fatal("This is a fatal error message");
  12.     }
  13. }
复制代码

log4net使用指南

log4net是另一个流行的日志记录框架,是Apache log4j框架的.NET移植版本。

通过NuGet安装log4net包:
  1. Install-Package log4net
复制代码

可以通过代码或配置文件来配置log4net。

在App.config或Web.config中添加log4net配置:
  1. <configuration>
  2.   <configSections>
  3.     <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  4.   </configSections>
  5.   
  6.   <log4net>
  7.     <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
  8.       <layout type="log4net.Layout.PatternLayout">
  9.         <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
  10.       </layout>
  11.     </appender>
  12.    
  13.     <appender name="FileAppender" type="log4net.Appender.FileAppender">
  14.       <file value="logs/application.log" />
  15.       <appendToFile value="true" />
  16.       <layout type="log4net.Layout.PatternLayout">
  17.         <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
  18.       </layout>
  19.     </appender>
  20.    
  21.     <root>
  22.       <level value="DEBUG" />
  23.       <appender-ref ref="ConsoleAppender" />
  24.       <appender-ref ref="FileAppender" />
  25.     </root>
  26.   </log4net>
  27. </configuration>
复制代码

然后在代码中使用:
  1. using log4net;
  2. using log4net.Config;
  3. public class Program
  4. {
  5.     private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
  6.     public static void Main()
  7.     {
  8.         // 配置log4net
  9.         XmlConfigurator.Configure();
  10.         // 记录日志
  11.         Log.Debug("This is a debug message");
  12.         Log.Info("This is an info message");
  13.         Log.Warn("This is a warning message");
  14.         Log.Error("This is an error message");
  15.         Log.Fatal("This is a fatal error message");
  16.     }
  17. }
复制代码

Serilog使用指南

Serilog是一个结构化日志记录库,支持丰富的日志数据结构。

通过NuGet安装Serilog包:
  1. Install-Package Serilog
  2. Install-Package Serilog.Sinks.Console
  3. Install-Package Serilog.Sinks.File
复制代码
  1. using Serilog;
  2. public class Program
  3. {
  4.     public static void Main()
  5.     {
  6.         // 配置Serilog
  7.         Log.Logger = new LoggerConfiguration()
  8.             .MinimumLevel.Debug()
  9.             .WriteTo.Console()
  10.             .WriteTo.File("logs/app-.txt", rollingInterval: RollingInterval.Day)
  11.             .CreateLogger();
  12.         // 记录结构化日志
  13.         Log.Information("Hello, {Name}!", "World");
  14.         // 记录带有属性的日志
  15.         var position = new { Latitude = 25, Longitude = 134 };
  16.         Log.Information("Processing {@Position}", position);
  17.         // 记录不同级别的日志
  18.         Log.Debug("This is a debug message");
  19.         Log.Information("This is an info message");
  20.         Log.Warning("This is a warning message");
  21.         Log.Error("This is an error message");
  22.         Log.Fatal("This is a fatal error message");
  23.         // 关闭日志
  24.         Log.CloseAndFlush();
  25.     }
  26. }
复制代码

日志框架比较与选择

实际应用案例:不同场景下的最佳实践

控制台应用程序中的输出

在控制台应用程序中,通常需要直接与用户交互,因此输出应该清晰、易读。
  1. using System;
  2. using System.Threading;
  3. public class ConsoleApp
  4. {
  5.     public static void Main()
  6.     {
  7.         // 设置控制台标题
  8.         Console.Title = "My Console Application";
  9.         // 显示欢迎信息
  10.         Console.WriteLine("======================================");
  11.         Console.WriteLine("=    Welcome to My Application     =");
  12.         Console.WriteLine("======================================");
  13.         Console.WriteLine();
  14.         // 模拟进度显示
  15.         Console.Write("Processing: ");
  16.         for (int i = 0; i <= 100; i += 10)
  17.         {
  18.             Console.Write($"{i}% ");
  19.             Thread.Sleep(200);
  20.         }
  21.         Console.WriteLine("\n");
  22.         // 彩色输出重要信息
  23.         Console.ForegroundColor = ConsoleColor.Green;
  24.         Console.WriteLine("Operation completed successfully!");
  25.         Console.ResetColor();
  26.         // 用户输入
  27.         Console.Write("Press any key to exit...");
  28.         Console.ReadKey();
  29.     }
  30. }
复制代码

ASP.NET Core应用程序中的日志记录

在ASP.NET Core应用程序中,日志记录对于监控和调试至关重要。
  1. using Microsoft.AspNetCore.Builder;
  2. using Microsoft.AspNetCore.Hosting;
  3. using Microsoft.Extensions.DependencyInjection;
  4. using Microsoft.Extensions.Hosting;
  5. using Microsoft.Extensions.Logging;
  6. using Serilog;
  7. public class Program
  8. {
  9.     public static void Main(string[] args)
  10.     {
  11.         // 配置Serilog
  12.         Log.Logger = new LoggerConfiguration()
  13.             .MinimumLevel.Information()
  14.             .WriteTo.Console()
  15.             .WriteTo.File("logs/webapp-.txt", rollingInterval: RollingInterval.Day)
  16.             .CreateLogger();
  17.         try
  18.         {
  19.             Log.Information("Starting web host");
  20.             CreateHostBuilder(args).Build().Run();
  21.         }
  22.         catch (Exception ex)
  23.         {
  24.             Log.Fatal(ex, "Host terminated unexpectedly");
  25.         }
  26.         finally
  27.         {
  28.             Log.CloseAndFlush();
  29.         }
  30.     }
  31.     public static IHostBuilder CreateHostBuilder(string[] args) =>
  32.         Host.CreateDefaultBuilder(args)
  33.             .UseSerilog() // 使用Serilog作为日志提供程序
  34.             .ConfigureWebHostDefaults(webBuilder =>
  35.             {
  36.                 webBuilder.UseStartup<Startup>();
  37.             });
  38. }
  39. public class Startup
  40. {
  41.     private readonly ILogger<Startup> _logger;
  42.     public Startup(ILogger<Startup> logger)
  43.     {
  44.         _logger = logger;
  45.     }
  46.     public void ConfigureServices(IServiceCollection services)
  47.     {
  48.         _logger.LogInformation("Configuring services");
  49.         // 配置服务
  50.     }
  51.     public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  52.     {
  53.         _logger.LogInformation("Configuring middleware");
  54.         
  55.         if (env.IsDevelopment())
  56.         {
  57.             _logger.LogDebug("In development environment");
  58.             app.UseDeveloperExceptionPage();
  59.         }
  60.         app.UseRouting();
  61.         
  62.         app.UseEndpoints(endpoints =>
  63.         {
  64.             endpoints.MapGet("/", async context =>
  65.             {
  66.                 _logger.LogInformation("Processing request to /");
  67.                 await context.Response.WriteAsync("Hello World!");
  68.             });
  69.         });
  70.     }
  71. }
复制代码

桌面应用程序中的调试输出

在桌面应用程序(如WPF或WinForms)中,调试输出对于开发过程中的问题诊断非常重要。
  1. using System;
  2. using System.Diagnostics;
  3. using System.Windows;
  4. namespace WpfApp
  5. {
  6.     public partial class MainWindow : Window
  7.     {
  8.         public MainWindow()
  9.         {
  10.             InitializeComponent();
  11.             Loaded += MainWindow_Loaded;
  12.         }
  13.         private void MainWindow_Loaded(object sender, RoutedEventArgs e)
  14.         {
  15.             // 使用Debug输出
  16.             Debug.WriteLine("Application started");
  17.             // 使用Trace输出
  18.             Trace.WriteLine("Window loaded");
  19.             // 条件输出
  20.             bool debugMode = true;
  21.             Debug.WriteLineIf(debugMode, "Debug mode is active");
  22.             // 性能测量
  23.             Stopwatch stopwatch = Stopwatch.StartNew();
  24.             // 执行一些操作
  25.             LoadData();
  26.             stopwatch.Stop();
  27.             Debug.WriteLine($"Data loaded in {stopwatch.ElapsedMilliseconds} ms");
  28.         }
  29.         private void LoadData()
  30.         {
  31.             // 模拟数据加载
  32.             for (int i = 0; i < 100; i++)
  33.             {
  34.                 Debug.WriteLine($"Loading item {i}");
  35.                 // 模拟工作
  36.                 System.Threading.Thread.Sleep(10);
  37.             }
  38.         }
  39.     }
  40. }
复制代码

库和API中的日志记录

在开发库和API时,提供适当的日志记录可以帮助使用者更好地理解和使用你的代码。
  1. using Microsoft.Extensions.Logging;
  2. using System;
  3. public class MyApiService
  4. {
  5.     private readonly ILogger<MyApiService> _logger;
  6.     public MyApiService(ILogger<MyApiService> logger)
  7.     {
  8.         _logger = logger ?? throw new ArgumentNullException(nameof(logger));
  9.     }
  10.     public void ProcessData(string data)
  11.     {
  12.         try
  13.         {
  14.             _logger.LogInformation("Starting data processing");
  15.             
  16.             if (string.IsNullOrEmpty(data))
  17.             {
  18.                 _logger.LogWarning("Empty data received");
  19.                 return;
  20.             }
  21.             // 模拟处理
  22.             _logger.LogDebug("Processing data: {Data}", data);
  23.             
  24.             // 如果启用了详细日志,输出更多细节
  25.             if (_logger.IsEnabled(LogLevel.Debug))
  26.             {
  27.                 _logger.LogDebug("Data length: {Length}", data.Length);
  28.             }
  29.             // 实际处理逻辑
  30.             var result = data.ToUpper();
  31.             
  32.             _logger.LogInformation("Data processed successfully");
  33.         }
  34.         catch (Exception ex)
  35.         {
  36.             _logger.LogError(ex, "Error processing data");
  37.             throw;
  38.         }
  39.     }
  40. }
复制代码

多线程应用程序中的日志记录

在多线程应用程序中,日志记录需要考虑线程安全和上下文信息。
  1. using System;
  2. using System.Diagnostics;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. public class MultiThreadedApp
  6. {
  7.     private static readonly object _lock = new object();
  8.    
  9.     public static void Main()
  10.     {
  11.         // 配置Trace监听器
  12.         Trace.Listeners.Add(new ConsoleTraceListener());
  13.         Trace.AutoFlush = true;
  14.         // 启动多个任务
  15.         Task[] tasks = new Task[5];
  16.         for (int i = 0; i < 5; i++)
  17.         {
  18.             int taskNum = i;
  19.             tasks[i] = Task.Run(() => ProcessData(taskNum));
  20.         }
  21.         // 等待所有任务完成
  22.         Task.WaitAll(tasks);
  23.         Console.WriteLine("All tasks completed");
  24.     }
  25.     private static void ProcessData(int taskNum)
  26.     {
  27.         // 使用CorrelationManager跟踪活动
  28.         Trace.CorrelationManager.ActivityId = Guid.NewGuid();
  29.         Trace.CorrelationManager.StartLogicalOperation($"Task{taskNum}");
  30.         try
  31.         {
  32.             Trace.WriteLine($"Task {taskNum} started on thread {Thread.CurrentThread.ManagedThreadId}");
  33.             // 模拟工作
  34.             for (int i = 0; i < 5; i++)
  35.             {
  36.                 // 线程安全的日志记录
  37.                 lock (_lock)
  38.                 {
  39.                     Trace.WriteLine($"Task {taskNum}, Step {i}");
  40.                 }
  41.                 Thread.Sleep(100);
  42.             }
  43.             Trace.WriteLine($"Task {taskNum} completed");
  44.         }
  45.         finally
  46.         {
  47.             Trace.CorrelationManager.StopLogicalOperation();
  48.         }
  49.     }
  50. }
复制代码

性能考虑和优化

日志记录的性能影响

日志记录可能会对应用程序性能产生影响,特别是在高负载或高频日志记录的情况下。以下是一些性能考虑因素:

使用异步日志记录可以减少I/O操作对主线程的影响。
  1. using System.Threading.Tasks;
  2. using NLog;
  3. public class AsyncLoggingExample
  4. {
  5.     private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
  6.     public static async Task ProcessDataAsync()
  7.     {
  8.         Logger.Info("Starting async processing");
  9.         
  10.         // 模拟异步工作
  11.         await Task.Delay(100);
  12.         
  13.         Logger.Info("Async processing completed");
  14.     }
  15. }
复制代码

在记录复杂或耗时的日志消息前,检查日志级别是否启用。
  1. using Microsoft.Extensions.Logging;
  2. public class ConditionalLoggingExample
  3. {
  4.     private readonly ILogger<ConditionalLoggingExample> _logger;
  5.     public ConditionalLoggingExample(ILogger<ConditionalLoggingExample> logger)
  6.     {
  7.         _logger = logger;
  8.     }
  9.     public void ProcessLargeData(LargeDataObject data)
  10.     {
  11.         _logger.LogInformation("Processing large data");
  12.         
  13.         // 在记录复杂对象前检查日志级别
  14.         if (_logger.IsEnabled(LogLevel.Debug))
  15.         {
  16.             // 序列化大型对象可能很耗时
  17.             _logger.LogDebug("Data details: {@Data}", data);
  18.         }
  19.         
  20.         // 处理数据...
  21.     }
  22. }
  23. public class LargeDataObject
  24. {
  25.     // 大型数据对象的属性
  26. }
复制代码

批量日志记录可以减少I/O操作次数,提高性能。
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Text;
  5. public class BatchLoggingExample
  6. {
  7.     private readonly string _logFilePath;
  8.     private readonly Queue<string> _logQueue;
  9.     private readonly object _lock;
  10.     private readonly int _batchSize;
  11.     private readonly TimeSpan _flushInterval;
  12.     private DateTime _lastFlushTime;
  13.     public BatchLoggingExample(string logFilePath, int batchSize = 100, TimeSpan? flushInterval = null)
  14.     {
  15.         _logFilePath = logFilePath;
  16.         _batchSize = batchSize;
  17.         _flushInterval = flushInterval ?? TimeSpan.FromSeconds(5);
  18.         _logQueue = new Queue<string>();
  19.         _lock = new object();
  20.         _lastFlushTime = DateTime.Now;
  21.     }
  22.     public void Log(string message)
  23.     {
  24.         lock (_lock)
  25.         {
  26.             _logQueue.Enqueue($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}");
  27.             // 检查是否需要刷新
  28.             if (_logQueue.Count >= _batchSize ||
  29.                 (DateTime.Now - _lastFlushTime) > _flushInterval)
  30.             {
  31.                 Flush();
  32.             }
  33.         }
  34.     }
  35.     public void Flush()
  36.     {
  37.         if (_logQueue.Count == 0)
  38.             return;
  39.         var sb = new StringBuilder();
  40.         while (_logQueue.Count > 0)
  41.         {
  42.             sb.AppendLine(_logQueue.Dequeue());
  43.         }
  44.         File.AppendAllText(_logFilePath, sb.ToString());
  45.         _lastFlushTime = DateTime.Now;
  46.     }
  47. }
复制代码

日志级别管理

合理使用日志级别可以减少不必要的日志记录,提高性能。
  1. using Microsoft.Extensions.Logging;
  2. public class LogLevelManagementExample
  3. {
  4.     private readonly ILogger<LogLevelManagementExample> _logger;
  5.     public LogLevelManagementExample(ILogger<LogLevelManagementExample> logger)
  6.     {
  7.         _logger = logger;
  8.     }
  9.     public void ProcessData(string data)
  10.     {
  11.         // 始终记录重要信息
  12.         _logger.LogInformation("Processing data");
  13.         // 仅在开发环境记录详细信息
  14.         _logger.LogDebug("Data details: {Data}", data);
  15.         try
  16.         {
  17.             // 处理数据...
  18.             
  19.             // 记录成功信息,但仅在Info级别启用时
  20.             if (_logger.IsEnabled(LogLevel.Information))
  21.             {
  22.                 _logger.LogInformation("Data processed successfully. Length: {Length}", data.Length);
  23.             }
  24.         }
  25.         catch (Exception ex)
  26.         {
  27.             // 始终记录错误
  28.             _logger.LogError(ex, "Error processing data");
  29.             throw;
  30.         }
  31.     }
  32. }
复制代码

结构化日志 vs. 纯文本日志

结构化日志(如JSON格式)虽然可能占用更多空间,但提供了更好的查询和分析能力。
  1. using Serilog;
  2. using System;
  3. using System.Collections.Generic;
  4. public class StructuredLoggingExample
  5. {
  6.     public static void Main()
  7.     {
  8.         Log.Logger = new LoggerConfiguration()
  9.             .WriteTo.Console()
  10.             .WriteTo.File("logs/structured.json", formatter: new Serilog.Formatting.Json.JsonFormatter())
  11.             .CreateLogger();
  12.         // 记录结构化日志
  13.         var order = new
  14.         {
  15.             Id = 12345,
  16.             Customer = "John Doe",
  17.             Items = new[] { "Item1", "Item2", "Item3" },
  18.             Total = 99.99,
  19.             Date = DateTime.Now
  20.         };
  21.         Log.Information("Order processed: {@Order}", order);
  22.         // 记录带有上下文的信息
  23.         Log.ForContext("Department", "Sales")
  24.            .ForContext("UserId", "user123")
  25.            .Information("User action: {Action} on {Target}", "Update", "Order");
  26.         Log.CloseAndFlush();
  27.     }
  28. }
复制代码

日志轮转和清理

长时间运行的应用程序需要适当的日志轮转和清理机制,以防止日志文件占用过多磁盘空间。
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. public class LogRotationExample
  5. {
  6.     private readonly string _logDirectory;
  7.     private readonly string _logFilePrefix;
  8.     private readonly int _maxLogFiles;
  9.     private readonly long _maxLogFileSize;
  10.     public LogRotationExample(string logDirectory, string logFilePrefix, int maxLogFiles = 10, long maxLogFileSize = 10 * 1024 * 1024)
  11.     {
  12.         _logDirectory = logDirectory;
  13.         _logFilePrefix = logFilePrefix;
  14.         _maxLogFiles = maxLogFiles;
  15.         _maxLogFileSize = maxLogFileSize;
  16.         // 确保日志目录存在
  17.         Directory.CreateDirectory(logDirectory);
  18.     }
  19.     public void Log(string message)
  20.     {
  21.         // 获取当前日志文件路径
  22.         string currentLogPath = GetCurrentLogFilePath();
  23.         // 检查当前日志文件是否存在以及是否超过大小限制
  24.         if (File.Exists(currentLogPath) && new FileInfo(currentLogPath).Length > _maxLogFileSize)
  25.         {
  26.             // 轮转日志文件
  27.             RotateLogFiles();
  28.             currentLogPath = GetCurrentLogFilePath();
  29.         }
  30.         // 写入日志
  31.         File.AppendAllText(currentLogPath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}{Environment.NewLine}");
  32.     }
  33.     private string GetCurrentLogFilePath()
  34.     {
  35.         return Path.Combine(_logDirectory, $"{_logFilePrefix}_current.log");
  36.     }
  37.     private void RotateLogFiles()
  38.     {
  39.         // 删除最旧的日志文件(如果超过最大数量)
  40.         var logFiles = Directory.GetFiles(_logDirectory, $"{_logFilePrefix}_*.log")
  41.                                .OrderBy(f => f)
  42.                                .ToList();
  43.         while (logFiles.Count >= _maxLogFiles)
  44.         {
  45.             File.Delete(logFiles[0]);
  46.             logFiles.RemoveAt(0);
  47.         }
  48.         // 重命名当前日志文件
  49.         string currentLogPath = GetCurrentLogFilePath();
  50.         string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
  51.         string archivedLogPath = Path.Combine(_logDirectory, $"{_logFilePrefix}_{timestamp}.log");
  52.         if (File.Exists(currentLogPath))
  53.         {
  54.             File.Move(currentLogPath, archivedLogPath);
  55.         }
  56.     }
  57. }
复制代码

总结

本文全面介绍了C#中输出窗口信息显示的各种方法,从基础的Console.WriteLine到高级的Debug调试技巧。我们探讨了以下关键内容:

1. 基础输出方法:详细介绍了Console.WriteLine及其相关方法,包括格式化输出、颜色设置和窗口操作。
2. 调试输出:深入讲解了Debug和Trace类的使用,包括配置、输出级别和性能分析。
3. 高级调试技巧:探讨了条件编译、Conditional特性、Debugger类的高级用法等。
4. 日志记录框架:比较和介绍了NLog、log4net和Serilog等流行日志框架的使用方法。
5. 实际应用案例:提供了不同场景下的最佳实践,包括控制台应用、ASP.NET Core应用、桌面应用、库和API以及多线程应用中的日志记录。
6. 性能考虑和优化:讨论了日志记录的性能影响,以及如何通过异步日志记录、条件日志记录、批量日志记录等技术优化性能。

基础输出方法:详细介绍了Console.WriteLine及其相关方法,包括格式化输出、颜色设置和窗口操作。

调试输出:深入讲解了Debug和Trace类的使用,包括配置、输出级别和性能分析。

高级调试技巧:探讨了条件编译、Conditional特性、Debugger类的高级用法等。

日志记录框架:比较和介绍了NLog、log4net和Serilog等流行日志框架的使用方法。

实际应用案例:提供了不同场景下的最佳实践,包括控制台应用、ASP.NET Core应用、桌面应用、库和API以及多线程应用中的日志记录。

性能考虑和优化:讨论了日志记录的性能影响,以及如何通过异步日志记录、条件日志记录、批量日志记录等技术优化性能。

通过掌握这些技术和方法,开发者可以更有效地输出和显示信息,提高调试效率,增强应用程序的可维护性和可监控性。无论是简单的控制台应用程序还是复杂的企业级系统,合理的输出和日志策略都是成功开发和维护软件的关键因素。

在实际开发中,应根据应用程序的需求、性能要求和运行环境选择合适的输出和日志记录方法。同时,始终记住日志记录的目的是为了帮助开发和运维人员更好地理解和维护系统,因此应保持日志信息的清晰、相关和可操作。
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则