C# API中AddJsonConsole日志输出Unicode而非字符的配置方案
问题
我在为C# API实现AddJsonConsole日志组件,目前已正常运行,但日志中的特殊字符会以Unicode值形式输出(例如用\u003E代替>)。请问是否可以通过配置让其输出实际字符?
我的Program.cs配置如下:
builder.Logging.ClearProviders(); builder.Logging.AddJsonConsole(options => { options.IncludeScopes = false; options.TimestampFormat = "HH:mm:ss"; options.JsonWriterOptions = new() { Indented = true, }; });
当前输出的日志示例(Exception字段存在大量Unicode转义值):
{ "Timestamp": "11.26.19", "EventId": 0, "LogLevel": "Error", "Category": "Mixorama.Server.Controllers.CocktailsController", "Message": "Failed to create cocktail", "Exception": "System.InvalidOperationException: The type of navigation \u0027CocktailEntity.Ingredients\u0027 is \u0027SelectListIterator\u003CIngredientEntity, IngredientEntity\u003E\u0027 which does not implement \u0027ICollection\u003CIngredientEntity\u003E\u0027. Collection navigations must implement \u0027ICollection\u003C\u003E\u0027 of the target type.\r\n at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrICollectionAccessor\u00603.GetCollection(Object instance)\r\n at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrICollectionAccessor\u00603.GetOrCreateCollection(Object instance, Boolean forMaterialization)\r\n at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrICollectionAccessor\u00603.Add(Object entity, Object value, Boolean forMaterialization)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.AddToCollection(INavigationBase navigationBase, Object value, Boolean forMaterialization)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.AddToCollection(InternalEntityEntry entry, INavigationBase navigation, InternalEntityEntry value, Boolean fromQuery)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.SetReferenceOrAddToCollection(InternalEntityEntry entry, INavigationBase navigation, InternalEntityEntry value, Boolean fromQuery)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.ToDependentFixup(InternalEntityEntry dependentEntry, InternalEntityEntry principalEntry, IForeignKey foreignKey, Boolean fromQuery)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.InitialFixup(InternalEntityEntry entry, InternalEntityEntry duplicateEntry, Boolean fromQuery)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.StateChanged(InternalEntityEntry entry, EntityState oldState, Boolean fromQuery)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.StateChanged(InternalEntityEntry entry, EntityState oldState, Boolean fromQuery)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.FireStateChanged(EntityState oldState)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges, Boolean modifyProperties)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges, Boolean modifyProperties, Nullable\u00601 forceStateWhenUnknownKey, Nullable\u00601 fallbackState)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode\u00601 node)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode\u00601 node, Func\u00602 handleNode)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, Boolean forceStateWhenUnknownKey)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.NavigationCollectionChanged(InternalEntityEntry entry, INavigationBase navigationBase, IEnumerable\u00601 added, IEnumerable\u00601 removed)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.NavigationCollectionChanged(InternalEntityEntry entry, INavigationBase navigationBase, IEnumerable\u00601 added, IEnumerable\u00601 removed)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectNavigationChange(InternalEntityEntry entry, INavigationBase navigationBase)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.LocalDetectChanges(InternalEntityEntry entry)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectChanges(IStateManager stateManager)\r\n at Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.DetectChanges()\r\n at Microsoft.EntityFrameworkCore.DbContext.TryDetectChanges()\r\n at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)\r\n at Mixorama.Server.Controllers.CocktailsController.CreateCocktail(CreateCocktailRequest request) in c:\Users\busks\Code\Mixorama\Mixorama.Server\Controllers\CocktailsController.cs:line 75", "State": { "Message": "Failed to create cocktail", "{OriginalFormat}": "Failed to create cocktail" } }
解决方案
可以通过配置JsonWriterOptions中的Encoder属性禁用Unicode转义,让日志直接输出实际字符。具体做法是将Encoder设置为JavaScriptEncoder.UnsafeRelaxedJsonEscaping,该编码器会直接输出ASCII范围内的特殊字符,而非转成Unicode值。
修改后的Program.cs配置如下:
using System.Text.Encodings.Web; // 需引入此命名空间 builder.Logging.ClearProviders(); builder.Logging.AddJsonConsole(options => { options.IncludeScopes = false; options.TimestampFormat = "HH:mm:ss"; options.JsonWriterOptions = new() { Indented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping // 新增配置项 }; });
说明
JavaScriptEncoder.UnsafeRelaxedJsonEscaping允许输出'、>、<等更多字符而不转义,提升日志可读性。- 注意名称中的"Unsafe":若日志内容可能包含不可信的用户输入,该配置存在XSS风险,但后端日志场景通常无此问题,可安全使用。
内容的提问来源于stack exchange,提问作者Jakob Busk Sørensen
相关产品推荐
相关产品推荐

