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

前言

本节我们来看看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中的最后几节关于死锁的内容,不管怎样都是在积累知识,你说呢,敬请期待!

时间: 2024-10-01 09:55:48

ASP.NET Core MVC之Serilog日志处理,你了解多少?的相关文章

解决ASP.NET Core Mvc文件上传限制问题实例_实用技巧

一.简介 在ASP.NET Core MVC中,文件上传的最大上传文件默认为20MB,如果我们想上传一些比较大的文件,就不知道怎么去设置了,没有了Web.Config我们应该如何下手呢? 二.设置上传文件大小 1.应用程序级别设置 我们需要在 ConfigureServices方法中添加如下代码,设置文件上传的大小限制为60 MB. public void ConfigureServices(IServiceCollection services) { servicesConfigure<For

ASP.NET Core MVC上传、导入、导出功能详解

前言 本君已成夜猫子,本节我们来讲讲ASP.NET Core MVC中的上传,这两天在研究批量导入功能,本节顺便简单搞搞导入.导出,等博主弄妥当了再来和大家一并分享. .NET Core MVC上传 首先我们来看看官网的上传的例子,再然后进行拓展训练,官网的表单是这样的. <form method="post" enctype="multipart/form-data" asp-controller="UploadFiles" asp-ac

ASP.NET Core MVC 配置全局路由前缀_实用技巧

ASP.NET Core MVC 配置全局路由前缀 前言 大家好,今天给大家介绍一个 ASP.NET Core MVC 的一个新特性,给全局路由添加统一前缀.严格说其实不算是新特性,不过是Core MVC特有的. 应用背景 不知道大家在做 Web Api 应用程序的时候,有没有遇到过这种场景,就是所有的接口都是以 /api 开头的,也就是我们的api 接口请求地址是像这样的: http://www.example.com/api/order/333 或者是这样的需求 http://www.exa

【翻译】在Visual Studio中使用Asp.Net Core MVC创建你的第一个Web API应用(一)

HTTP is not just for serving up web pages. It's also a powerful platform for building APIs that expose services and data. HTTP is simple, flexible, and ubiquitous. Almost any platform that you can think of has an HTTP library, so HTTP services can re

ASP.NET Core MVC压缩样式、脚本及总是复制文件到输出目录

前言 在.NET Core之前对于压缩样式文件和脚本我们可能需要借助第三方工具来进行压缩,但在ASP.NET MVC Core中则无需借助第三方工具来完成,本节我们来看看ASP.NET Core MVC为我们提供了哪些方便. 自动压缩样式和脚本 当我们在测试环境中肯定不需要压缩脚本的,如果一旦压缩脚本的话,若在控制台出现错误不利于我们调试,但是在生产环境中我们通过压缩脚本或者样式一来可以减少传输流量,二来可以加速页面加载时间,换句话说,此时我们需要测试环境和生产环境对应的原生版本和压缩版本,那么

ASP.NET Core MVC之ViewComponents(视图组件)知多少?

前言 大概一个来星期未更新博客了,久违了各位,关于SQL Server性能优化会和ASP.NET Core MVC穿插来讲,如果你希望我分享哪些内容可以在评论下方提出来,我会筛选并看看技术文档来对你的内容进行解答,借此希望我们能共同进步和学习.本节我们来讲讲ASP.NET Core  MVC中的视图组件. Web应用程序下管理ViewComponents 我是奔着项目用到了哪些就会去写对应的技术博客,在我们项目中利用视图组件来加载权限菜单,这一块是我老大所做,我也就粗略看了看使用方法并未深入借此

ASP.NET Core MVC上传、导入、导出知多少

前言 本君已成夜猫子,本节我们来讲讲ASP.NET Core MVC中的上传,这两天在研究批量导入功能,本节顺便简单搞搞导入.导出,等博主弄妥当了再来和大家一并分享. .NET Core MVC上传 首先我们来看看官网的上传的例子,再然后进行拓展训练,官网的表单是这样的. <form method="post" enctype="multipart/form-data" asp-controller="UploadFiles" asp-ac

ASP.NET Core MVC/WebAPi如何构建路由?

前言 本节我们来讲讲ASP.NET Core中的路由,在讲路由之前我们首先回顾下之前所讲在ASP.NET Core中的模型绑定这其中有一个问题是我在项目当中遇见的,我们下面首先来看看这个问题. 回顾ASP.NET Core模型绑定 我们有这样一个场景:修改个人资料中的各个属性,此时每个属性的值的类型肯定是不一样的,所以我们将值定义为object,如下model. public class BlogViewModel { public string prop { get; set; } publi

ASP.NET Core MVC/WebAPi 模型绑定探索

前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用到了,你再去看理论性的文章时才会豁然开朗,这是我一直以来学习技术的方法.本文我们来讲解.NET Core中的模型绑定. 话题 在ASP.NET Core之前MVC和Web APi被分开,也就说其请求管道是独立的,而在ASP.NET Core中,WebAPi和MVC的请求管道被合并在一起,当我们建立控