ASP.NET Core MVC之Serilog日志处理,你了解多少?

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:

前言

本节我们来看看ASP.NET Core MVC中比较常用的功能,对于导入和导出目前仍在探索中,项目需要自定义列合并,所以事先探索了如何在ASP.NET Core MVC进行导入、导出,更高级的内容还需等我学习再给出。

EntityFramework Core

在学习ASP.NET Core MVC之前我们来看看在EF Core中如何更新对象指定属性,这个问题之前我们已经探讨过,但是还是存在一点问题,请往下看。

复制代码
        public void Update(T entity, params Expression<Func<T, object>>[] properties)
        {
            _context.Entry(entity).State = EntityState.Unchanged;
            foreach (var property in properties)
            {
                var propertyName = ExpressionHelper.GetExpressionText(property);
                _context.Entry(entity).Property(propertyName).IsModified = true;
            }
        }
复制代码

上述方法可以用来更新对象指定属性,使用lambda来指定需要更新的属性,如下:

复制代码
        [HttpGet("[action]")]
        public IActionResult Index()
        {
            var blog = new Blog() { Id = 1, Name = "Jeffcky1" };
            _blogRepository.Update(blog, d => d.Name);
            _blogRepository.SaveChanges();
        }
复制代码

但是当更新数值类型时会解析不到该属性,如下:

             var blog = new Blog() { Id = 1, Count = 1 };
            _blogRepository.Update(blog, d => d.Count);
            _blogRepository.SaveChanges();

此时得到该属性名称为空字符串

 

lambda为何解析不到呢?原来它进行了Convert如下:

既然是将其转换成了Convert,那么在表达式树中应该用其节点类型,如下:

此时我们需要判断其节点类型是否已经经过Convert即可,最终代码如下:

复制代码
        public void Update(T entity, params Expression<Func<T, object>>[] properties)
        {

            EntityEntry<T> entry = _context.Entry(entity);
            entry.State = EntityState.Unchanged;
            foreach (var property in properties)
            {
                string propertyName = "";
                Expression bodyExpression = property.Body;
                if (bodyExpression.NodeType == ExpressionType.Convert && bodyExpression is UnaryExpression)
                {
                    Expression operand = ((UnaryExpression)property.Body).Operand;
                    propertyName = ((MemberExpression)operand).Member.Name;
                }
                else
                {
                    propertyName = ExpressionHelper.GetExpressionText(property);
                }
                entry.Property(propertyName).IsModified = true;
            }
       }
复制代码

此时将能正确解析到数值类型名称,如下:

为何要封装这一层,只是不想首先查询出对象然后再来进行更新属性,其两步操作合并为一步岂不爽哉。当然我们也可以不通过lambda来实现,直接给出属性集合,然后遍历即可,大概如下:

复制代码
        public void Update(T entity, params object[] properties)
        {
            _context.ChangeTracker.TrackGraph(entity, e =>
            {
                e.Entry.State = EntityState.Unchanged;
                if ((e.Entry.Entity as T) != null)
                {
                    foreach (var p in properties)
                    {
                        _context.Entry(e.Entry.Entity as T).Property(p.ToString()).IsModified = true;
                    }

                }
            });
        }
复制代码

Serilog日志输出 

目前比较流行的日志框架则是Log4net、NLog,之前也一直用的Log4net,但是在.net core中已经内置了日志框架Serilog,在github上也已加星不少,想必比较强大,既然这样为何不使用内置的呢,免去再用其他日志框架还要配置的麻烦。

创建默认项目在Startup中已经注入日志,如下:

loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();

当利用dotnet.exe调试时在控制台会显示调试各种信息,要是直接运行那该如何呢?在web中的配置文件web.config中,如下:

 <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
  </system.webServer>

我们需要设置 stdoutLogEnabled="true" 来启动调试,此时运行项目你会发现屁都没有,此时我们查看windows日志发现根本无法创建文件夹,想必是没有权限的缘故。

此时我们需要手动创建logs文件夹,最终运行程序则会自动将所有信息写入到日志文件中,如下:

是不是这样就完了呢,这样设置则会将所有信息都会写入日志那样岂不是额外做些无用功,看看如下生成的日志。

 

光说不练假把式,来,我们来实现一个,让你见识见识Serilog的强大。先定义一个自定义输出格式的Option并创建输出日志文件夹。

复制代码
    public class JeffckyLogOptions
    {
        public string LogPath { get; set; } = @"C:\Jeffcky_StudyEFCore\logs";

        public string PathFormat { get; set; }

        public static void EnsurePreConditions(JeffckyLogOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            if (string.IsNullOrWhiteSpace(options.LogPath))
            {
                throw new ArgumentException("系统日志文件存储路径未配置", nameof(options.LogPath));
            }

            if (string.IsNullOrWhiteSpace(options.PathFormat))
            {
                throw new ArgumentException("系统日志文件名称未配置", nameof(options.PathFormat));
            }

            if (!Directory.Exists(options.LogPath))
            {
                Directory.CreateDirectory(options.LogPath);
            }
        }
    }
复制代码

内置实现日志的接口.net core已经给出,所以我们需要获取内置日志接口服务并实现自定义扩展方法。

app.ApplicationServices.GetService(typeof(ILoggerFactory));

此时上述app来源于 IApplicationBuilder 此时我们则只需要实现该接口的自定义扩展方法。我们开始下载Serilog程序包

此时我们需要对日志通过 LoggerConfiguration 类进行初始化配置。

接下来则是将日志写到文件中我们可以自定义格式,此时再下载如下程序包。

里面有个 RollingFile 方法来自定输出日志模板,自此我们就有了如下代码:

          var serilog = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .Enrich.FromLogContext()
                .WriteTo.RollingFile(Path.Combine(options.LogPath, options.PathFormat),
                    outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {Message}{NewLine}{Exception}");

我们获取.net core内置的日志接口服务。

 ILoggerFactory loggerFactory = (ILoggerFactory)app.ApplicationServices.GetService(typeof(ILoggerFactory));

同时设置我们输出日志的路径选项。

JeffckyLogOptions.EnsurePreConditions(options);

接下来将Serilog创建的日志添加到内置日志服务中,如下:

 loggerFactory.AddSerilog(serilog.CreateLogger());

当然上述AddSerilog方法时扩展方法,此时我们仍需要添加如下扩展程序包:

最后还差一步则是日志的生命周期,灰常重要,为什么说灰常重要,我们要确保当程序关闭时或者系统出问题时所有在内存缓冲区的日志都将输送到日志文件夹中,如下:

复制代码
            // Ensure any buffered events are sent at shutdown
            IApplicationLifetime appLifetime = (IApplicationLifetime)app.ApplicationServices.GetService(typeof(IApplicationLifetime));
            if (appLifetime != null)
            {
                appLifetime.ApplicationStopped.Register(Log.CloseAndFlush);
            }
复制代码

一气呵成,最终我们在Startup的如下方法中进行调用即可:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {...}

利用Serilog添加自定日志格式输出,进行如下调用:

            app.UseJeffckySelfDefineLog(new JeffckyLogOptions()
            {
                LogPath = Configuration[nameof(JeffckyLogOptions.LogPath)],
                PathFormat = "Jeffcky_StudyEFCore_{Date}.log"
            });

当然还有其他强大功能,比如Serilog中可以获取到上下文,这样的话我们就可以过滤对于那个应用程序使用对应的日志输出级别以及其他,如下:

复制代码
                serilog = serilog.Filter.ByIncludingOnly((e) =>
                {
                    var context = e.Properties["SourceContext"].ToString();

                    return (context.StartsWith("\"Your Applicion Name") ||
                            e.Level == LogEventLevel.Warning ||
                            e.Level == LogEventLevel.Error ||
                            e.Level == LogEventLevel.Fatal);
                });
复制代码

说一千到一万,出现结果才是真理,我们一起来看看。

 

看看日志输出信息,你会看到干净的日志输出。

总结

本节我们详细讲解了.net core中新生代日志框架-Serilog,别抱着Log4net不放了,Serilog你值得拥有,跟着老大一直学习中,涨涨见识!因为项目中会用到批量导入和导出,所以在研究导出、导入,下节可能会讲到导入、导出在ASP.NET Core MVC中,也有可能会讲SQL Server中的最后几节关于死锁的内容,不管怎样都是在积累知识,你说呢,敬请期待!







本文转自Jeffcky博客园博客,原文链接:http://www.cnblogs.com/CreateMyself/p/6438068.html,如需转载请自行联系原作者

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
2月前
|
JSON 安全 API
.net 自定义日志类
在.NET中,创建自定义日志类有助于更好地管理日志信息。示例展示了如何创建、配置和使用日志记录功能,包括写入日志文件、设置日志级别、格式化消息等。注意事项涵盖时间戳、日志级别、JSON序列化、线程安全、日志格式、文件处理及示例使用。请根据需求调整代码。
54 13
|
2月前
|
开发框架 算法 中间件
ASP.NET Core 中的速率限制中间件
在ASP.NET Core中,速率限制中间件用于控制客户端请求速率,防止服务器过载并提高安全性。通过`AddRateLimiter`注册服务,并配置不同策略如固定窗口、滑动窗口、令牌桶和并发限制。这些策略可在全局、控制器或动作级别应用,支持自定义响应处理。使用中间件`UseRateLimiter`启用限流功能,并可通过属性禁用特定控制器或动作的限流。这有助于有效保护API免受滥用和过载。 欢迎关注我的公众号:Net分享 (239字符)
64 1
|
2月前
|
开发框架 .NET 开发者
简化 ASP.NET Core 依赖注入(DI)注册-Scrutor
Scrutor 是一个简化 ASP.NET Core 应用程序中依赖注入(DI)注册过程的开源库,支持自动扫描和注册服务。通过简单的配置,开发者可以轻松地从指定程序集中筛选、注册服务,并设置其生命周期,同时支持服务装饰等高级功能。适用于大型项目,提高代码的可维护性和简洁性。仓库地址:&lt;https://github.com/khellang/Scrutor&gt;
61 5
|
2月前
|
开发框架 缓存 .NET
GraphQL 与 ASP.NET Core 集成:从入门到精通
本文详细介绍了如何在ASP.NET Core中集成GraphQL,包括安装必要的NuGet包、创建GraphQL Schema、配置GraphQL服务等步骤。同时,文章还探讨了常见问题及其解决方法,如处理复杂查询、错误处理、性能优化和实现认证授权等,旨在帮助开发者构建灵活且高效的API。
59 3
|
3月前
|
开发框架 .NET C#
在 ASP.NET Core 中创建 gRPC 客户端和服务器
本文介绍了如何使用 gRPC 框架搭建一个简单的“Hello World”示例。首先创建了一个名为 GrpcDemo 的解决方案,其中包含一个 gRPC 服务端项目 GrpcServer 和一个客户端项目 GrpcClient。服务端通过定义 `greeter.proto` 文件中的服务和消息类型,实现了一个简单的问候服务 `GreeterService`。客户端则通过 gRPC 客户端库连接到服务端并调用其 `SayHello` 方法,展示了 gRPC 在 C# 中的基本使用方法。
64 5
在 ASP.NET Core 中创建 gRPC 客户端和服务器
|
4月前
|
开发框架 JavaScript 前端开发
一个适用于 ASP.NET Core 的轻量级插件框架
一个适用于 ASP.NET Core 的轻量级插件框架
|
4月前
|
存储 开发框架 JSON
ASP.NET Core OData 9 正式发布
【10月更文挑战第8天】Microsoft 在 2024 年 8 月 30 日宣布推出 ASP.NET Core OData 9,此版本与 .NET 8 的 OData 库保持一致,改进了数据编码以符合 OData 规范,并放弃了对旧版 .NET Framework 的支持,仅支持 .NET 8 及更高版本。新版本引入了更快的 JSON 编写器 `System.Text.UTF8JsonWriter`,优化了内存使用和序列化速度。
116 0
|
5月前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
|
12天前
|
存储 SQL 关系型数据库
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log、原理、写入过程;binlog与redolog区别、update语句的执行流程、两阶段提交、主从复制、三种日志的使用场景;查询日志、慢查询日志、错误日志等其他几类日志
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
|
1月前
|
SQL 关系型数据库 MySQL
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
MySQL事务日志-Undo Log工作原理分析

热门文章

最新文章