diff --git a/.gitignore b/.gitignore index b7645c4..4f554be 100644 --- a/.gitignore +++ b/.gitignore @@ -356,3 +356,5 @@ Blog.Core/Blog.Core*.xml Blog.Core.Api/WMBlog.db Blog.Core.Api/wwwroot/ui/ *.db +/Blog.Core.Api/WMBlog.db-journal +Logs diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index 77d681f..3bf6439 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -26,21 +26,25 @@ + + + + @@ -51,8 +55,6 @@ - - diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 89cb321..488903a 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -760,7 +760,7 @@ Values控制器 - + ValuesController diff --git a/Blog.Core.Api/Controllers/ValuesController.cs b/Blog.Core.Api/Controllers/ValuesController.cs index 1347ca1..677eb13 100644 --- a/Blog.Core.Api/Controllers/ValuesController.cs +++ b/Blog.Core.Api/Controllers/ValuesController.cs @@ -1,7 +1,7 @@ using AutoMapper; using Blog.Core.Common; using Blog.Core.Common.HttpContextUser; -using Blog.Core.Common.HttpPolly; +using Blog.Core.Common.Https.HttpPolly; using Blog.Core.Common.WebApiClients.HttpApis; using Blog.Core.EventBus; using Blog.Core.EventBus.EventHandling; diff --git a/Blog.Core.Api/Filter/GlobalExceptionFilter.cs b/Blog.Core.Api/Filter/GlobalExceptionFilter.cs index da119a6..44c6124 100644 --- a/Blog.Core.Api/Filter/GlobalExceptionFilter.cs +++ b/Blog.Core.Api/Filter/GlobalExceptionFilter.cs @@ -3,14 +3,10 @@ using Blog.Core.Common.Helper; using Blog.Core.Common.LogHelper; using Blog.Core.Hubs; using Blog.Core.Model; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.Logging; using StackExchange.Profiling; -using System; namespace Blog.Core.Filter { @@ -54,7 +50,7 @@ namespace Blog.Core.Filter MiniProfiler.Current.CustomTiming("Errors:", json.msg); - //采用log4net 进行错误日志记录 + //进行错误日志记录 _loggerHelper.LogError(json.msg + WriteLog(json.msg, context.Exception)); if (AppSettings.app(new string[] { "Middleware", "SignalRSendLog", "Enabled" }).ObjToBool()) { diff --git a/Blog.Core.Api/Log4net.config b/Blog.Core.Api/Log4net.config deleted file mode 100644 index 61bd373..0000000 --- a/Blog.Core.Api/Log4net.config +++ /dev/null @@ -1,364 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index f5223be..f7790a8 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -1,20 +1,16 @@ // 以下为asp.net 6.0的写法,如果用5.0,请看Program.five.cs文件 -using System.IdentityModel.Tokens.Jwt; -using System.Reflection; -using System.Text; + using Autofac; using Autofac.Extensions.DependencyInjection; using Blog.Core; using Blog.Core.Common; using Blog.Core.Common.Core; -using Blog.Core.Common.LogHelper; using Blog.Core.Extensions; using Blog.Core.Extensions.Apollo; using Blog.Core.Extensions.Middlewares; +using Blog.Core.Extensions.ServiceExtensions; using Blog.Core.Filter; using Blog.Core.Hubs; -using Blog.Core.IServices; -using Blog.Core.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Server.Kestrel.Core; @@ -22,34 +18,38 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; +using Serilog; +using System.IdentityModel.Tokens.Jwt; +using System.Reflection; +using System.Text; +using Blog.Core.Common.Https; +using Blog.Core.Serilog.Utility; var builder = WebApplication.CreateBuilder(args); + + // 1、配置host与容器 builder.Host -.UseServiceProviderFactory(new AutofacServiceProviderFactory()) -.ConfigureContainer(builder => -{ - builder.RegisterModule(new AutofacModuleRegister()); - builder.RegisterModule(); -}) -.ConfigureLogging((hostingContext, builder) => -{ - builder.AddFilter("System", LogLevel.Error); - builder.AddFilter("Microsoft", LogLevel.Error); - builder.SetMinimumLevel(LogLevel.Error); - builder.AddLog4Net(Path.Combine(Directory.GetCurrentDirectory(), "Log4net.config")); -}) -.ConfigureAppConfiguration((hostingContext, config) => -{ - config.Sources.Clear(); - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false); - config.AddConfigurationApollo("appsettings.apollo.json"); -}); + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureContainer(builder => + { + builder.RegisterModule(new AutofacModuleRegister()); + builder.RegisterModule(); + }) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.Sources.Clear(); + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false); + config.AddConfigurationApollo("appsettings.apollo.json"); + }); +builder.ConfigureApplication(); // 2、配置服务 builder.Services.AddSingleton(new AppSettings(builder.Configuration)); -builder.Services.AddSingleton(new LogLock(builder.Environment.ContentRootPath)); + + + builder.Services.AddUiFilesZipSetup(builder.Environment); Permissions.IsUseIds4 = AppSettings.app(new string[] { "Startup", "IdentityServer4", "Enabled" }).ObjToBool(); @@ -62,6 +62,9 @@ builder.Services.AddMemoryCacheSetup(); builder.Services.AddRedisCacheSetup(); builder.Services.AddSqlsugarSetup(); builder.Services.AddDbSetup(); + +builder.Host.AddSerilogSetup(); + builder.Services.AddAutoMapperSetup(); builder.Services.AddCorsSetup(); builder.Services.AddMiniProfilerSetup(); @@ -92,34 +95,34 @@ builder.Services.AddIpPolicyRateLimitSetup(builder.Configuration); builder.Services.AddSignalR().AddNewtonsoftJsonProtocol(); builder.Services.AddScoped(); builder.Services.Configure(x => x.AllowSynchronousIO = true) - .Configure(x => x.AllowSynchronousIO = true); + .Configure(x => x.AllowSynchronousIO = true); builder.Services.AddDistributedMemoryCache(); builder.Services.AddSession(); builder.Services.AddHttpPollySetup(); builder.Services.AddControllers(o => -{ - o.Filters.Add(typeof(GlobalExceptionsFilter)); - //o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention()); - o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name))); -}) -.AddNewtonsoftJson(options => -{ - options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - options.SerializerSettings.ContractResolver = new DefaultContractResolver(); - options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; - //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; - options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; - options.SerializerSettings.Converters.Add(new StringEnumConverter()); -}) -//.AddFluentValidation(config => -//{ -// //程序集方式添加验证 -// config.RegisterValidatorsFromAssemblyContaining(typeof(UserRegisterVoValidator)); -// //是否与MvcValidation共存 -// config.DisableDataAnnotationsValidation = true; -//}) -; + { + o.Filters.Add(typeof(GlobalExceptionsFilter)); + //o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention()); + o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name))); + }) + .AddNewtonsoftJson(options => + { + options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + options.SerializerSettings.ContractResolver = new DefaultContractResolver(); + options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; + options.SerializerSettings.Converters.Add(new StringEnumConverter()); + }) + //.AddFluentValidation(config => + //{ + // //程序集方式添加验证 + // config.RegisterValidatorsFromAssemblyContaining(typeof(UserRegisterVoValidator)); + // //是否与MvcValidation共存 + // config.DisableDataAnnotationsValidation = true; + //}) + ; builder.Services.AddEndpointsApiExplorer(); @@ -159,12 +162,23 @@ app.UseDefaultFiles(defaultFilesOptions); app.UseStaticFiles(); app.UseCookiePolicy(); app.UseStatusCodePages(); +app.UseSerilogRequestLogging(options => +{ + options.GetLevel = SerilogRequestUtility.GetRequestLevel; + options.EnrichDiagnosticContext = (diagnosticContext, httpContext) => + { + diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value); + diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme); + diagnosticContext.Set("RequestIp", httpContext.GetRequestIp()); + }; +}); app.UseRouting(); if (builder.Configuration.GetValue("AppSettings:UseLoadTest")) { app.UseMiddleware(); } + app.UseAuthentication(); app.UseAuthorization(); app.UseMiniProfilerMiddleware(); diff --git a/Blog.Core.Api/Startup.cs b/Blog.Core.Api/Startup.cs index bc1630f..1364d6a 100644 --- a/Blog.Core.Api/Startup.cs +++ b/Blog.Core.Api/Startup.cs @@ -39,7 +39,6 @@ namespace Blog.Core { // 以下code可能与文章中不一样,对代码做了封装,具体查看右侧 Extensions 文件夹. services.AddSingleton(new AppSettings(Configuration)); - services.AddSingleton(new LogLock(Env.ContentRootPath)); services.AddUiFilesZipSetup(Env); Permissions.IsUseIds4 = AppSettings.app(new string[] { "Startup", "IdentityServer4", "Enabled" }).ObjToBool(); diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index 3c11737..c712054 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -1,98 +1,92 @@ { - "urls": "http://*:9291", //web服务端口,如果用IIS部署,把这个去掉 - "Logging": { - "LogLevel": { - "Default": "Information", //加入Default否则log4net本地写入不了日志 - "Blog.Core.AuthHelper.ApiResponseHandler": "Error" - }, - "Debug": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning" - } - }, - "Console": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning", - "Microsoft.Hosting.Lifetime": "Debug" - } - }, - "Log4Net": { - "Name": "Blog.Core" - } + "urls": "http://*:9291", //web服务端口,如果用IIS部署,把这个去掉 + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Information", + "Microsoft.AspNetCore": "Warning", + "System": "Warning", + "System.Net.Http.HttpClient": "Warning", + "Hangfire": "Information", + "Magicodes": "Warning", + "DotNetCore.CAP": "Information", + "Savorboard.CAP": "Information", + "Quartz": "Information" + } + } + }, + "AllowedHosts": "*", + "Redis": { + "ConnectionString": "127.0.0.1:6319,password=admin" + }, + "RabbitMQ": { + "Enabled": false, + "Connection": "118.25.251.13", + "UserName": "", + "Password": "!", + "RetryCount": 3 + }, + "Kafka": { + "Enabled": false, + "Servers": "localhost:9092", + "Topic": "blog", + "GroupId": "blog-consumer", + "NumPartitions": 3 //主题分区数量 + }, + "EventBus": { + "Enabled": false, + "SubscriptionClientName": "Blog.Core" + }, + "AppSettings": { + "RedisCachingAOP": { + "Enabled": false }, - "AllowedHosts": "*", - "Redis": { - "ConnectionString": "127.0.0.1:6319,password=admin" + "MemoryCachingAOP": { + "Enabled": true }, - "RabbitMQ": { - "Enabled": false, - "Connection": "118.25.251.13", - "UserName": "", - "Password": "!", - "RetryCount": 3 + "LogAOP": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": true + } }, - "Kafka": { - "Enabled": false, - "Servers": "localhost:9092", - "Topic": "blog", - "GroupId": "blog-consumer", - "NumPartitions": 3 //主题分区数量 + "TranAOP": { + "Enabled": true }, - "EventBus": { - "Enabled": false, - "SubscriptionClientName": "Blog.Core" - }, - "AppSettings": { - "RedisCachingAOP": { - "Enabled": false - }, - "MemoryCachingAOP": { - "Enabled": true - }, - "LogAOP": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": true - } - }, - "TranAOP": { - "Enabled": true - }, - "SqlAOP": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": false - }, - "LogToConsole": { - "Enabled": true - } - }, - "Date": "2018-08-28", - "SeedDBEnabled": true, //只生成表结构 - "SeedDBDataEnabled": true, //生成表,并初始化数据 - "Author": "Blog.Core", - "SvcName": "", // /svc/blog - "UseLoadTest": false + "SqlAOP": { + "Enabled": true, + "LogToFile": { + "Enabled": true + }, + "LogToDB": { + "Enabled": false + }, + "LogToConsole": { + "Enabled": true + } }, + "Date": "2018-08-28", + "SeedDBEnabled": true, //只生成表结构 + "SeedDBDataEnabled": true, //生成表,并初始化数据 + "Author": "Blog.Core", + "SvcName": "", // /svc/blog + "UseLoadTest": false + }, - // 请配置MainDB为你想要的主库的ConnId值,并设置对应的Enabled为true; - // *** 单库操作,把 MutiDBEnabled 设为false ***; - // *** 多库操作,把 MutiDBEnabled 设为true,其他的从库Enabled也为true **; - // 具体配置看视频:https://www.bilibili.com/video/BV1BJ411B7mn?p=6 - - "MainDB": "WMBLOG_SQLITE", //当前项目的主库,所对应的连接字符串的Enabled必须为true - "MutiDBEnabled": false, //是否开启多库模式 - "CQRSEnabled": false, //是否开启读写分离模式,必须是单库模式,且数据库类型一致,比如都是SqlServer - "DBS": [ - /* + // 请配置MainDB为你想要的主库的ConnId值,并设置对应的Enabled为true; + // *** 单库操作,把 MutiDBEnabled 设为false ***; + // *** 多库操作,把 MutiDBEnabled 设为true,其他的从库Enabled也为true **; + // 具体配置看视频:https://www.bilibili.com/video/BV1BJ411B7mn?p=6 + //Log:日志库; + "MainDB": "WMBLOG_SQLITE", //当前项目的主库,所对应的连接字符串的Enabled必须为true + "MutiDBEnabled": true, //是否开启多库模式 + "CQRSEnabled": false, //是否开启读写分离模式,必须是单库模式,且数据库类型一致,比如都是SqlServer + "DBS": [ + /* 对应下边的 DBType MySql = 0, SqlServer = 1, @@ -102,225 +96,232 @@ Dm = 5,//达梦 Kdbndp = 6,//人大金仓 */ - { - "ConnId": "WMBLOG_SQLITE", - "DBType": 2, - "Enabled": true, - "HitRate": 50, // 值越大,优先级越高 - "Connection": "WMBlog.db" //sqlite只写数据库名就行 - }, - { - "ConnId": "WMBLOG_MSSQL_1", - "DBType": 1, - "Enabled": false, - "HitRate": 40, - "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_1;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", - "ProviderName": "System.Data.SqlClient" - }, - { - "ConnId": "WMBLOG_MSSQL_2", - "DBType": 1, - "Enabled": false, - "HitRate": 30, - "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_2;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", - "ProviderName": "System.Data.SqlClient" - }, - { - "ConnId": "WMBLOG_MYSQL", - "DBType": 0, - "Enabled": false, - "HitRate": 20, - "Connection": "server=localhost;Database=blog;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" - }, - { - "ConnId": "WMBLOG_MYSQL_2", - "DBType": 0, - "Enabled": false, - "HitRate": 20, - "Connection": "server=localhost;Database=blogcore001;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" - }, - { - "ConnId": "WMBLOG_ORACLE", - "DBType": 3, - "Enabled": false, - "HitRate": 10, - "Connection": "Data Source=127.0.0.1/ops;User ID=OPS;Password=123456;Persist Security Info=True;Connection Timeout=60;" - }, - { - "ConnId": "WMBLOG_DM", - "DBType": 5, - "Enabled": false, - "HitRate": 10, - "Connection": "PORT=5236;DATABASE=DAMENG;HOST=localhost;PASSWORD=SYSDBA;USER ID=SYSDBA;" - }, - { - "ConnId": "WMBLOG_KDBNDP", - "DBType": 6, - "Enabled": false, - "HitRate": 10, - "Connection": "Server=127.0.0.1;Port=54321;UID=SYSTEM;PWD=system;database=SQLSUGAR4XTEST1;" - } - ], - "Audience": { - "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", //不要太短,16位+ - "SecretFile": "C:\\my-file\\blog.core.audience.secret.txt", //安全。内容就是Secret - "Issuer": "Blog.Core", - "Audience": "wr" + { + "ConnId": "WMBLOG_SQLITE", + "DBType": 2, + "Enabled": true, + "HitRate": 50, // 值越大,优先级越高 + "Connection": "WMBlog.db" //sqlite只写数据库名就行 }, - "Mongo": { - "ConnectionString": "mongodb://nosql.data", - "Database": "BlogCoreDb" + { + "ConnId": "Log", + "DBType": 2, + "Enabled": true, + "HitRate": 50, // 值越大,优先级越高 + "Connection": "WMBlogLog.db" //sqlite只写数据库名就行 }, - "Startup": { - "Domain": "http://localhost:9291", - "Cors": { - "PolicyName": "CorsIpAccess", //策略名称 - "EnableAllIPs": false, //当为true时,开放所有IP均可访问。 - // 支持多个域名端口,注意端口号后不要带/斜杆:比如localhost:8000/,是错的 - // 注意,http://127.0.0.1:1818 和 http://localhost:1818 是不一样的 - "IPs": "http://127.0.0.1:2364,http://localhost:2364,http://127.0.0.1:6688,http://localhost:6688" - }, - "AppConfigAlert": { - "Enabled": true - }, - "ApiName": "Blog.Core", - "IdentityServer4": { - "Enabled": false, // 这里默认是false,表示使用jwt,如果设置为true,则表示系统使用Ids4模式 - "AuthorizationUrl": "http://localhost:5004", // 认证中心域名 - "ApiName": "blog.core.api" // 资源服务器 - }, - "Authing": { - "Enabled": false, - "Issuer": "https://uldr24esx31h-demo.authing.cn/oidc", - "Audience": "63d51c4205c2849803be5178", - "JwksUri": "https://uldr24esx31h-demo.authing.cn/oidc/.well-known/jwks.json" - }, - "RedisMq": { - "Enabled": false //redis 消息队列 - }, - "MiniProfiler": { - "Enabled": false //性能分析开启 - }, - "Nacos": { - "Enabled": false //Nacos注册中心 - } + { + "ConnId": "WMBLOG_MSSQL_1", + "DBType": 1, + "Enabled": false, + "HitRate": 40, + "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_1;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", + "ProviderName": "System.Data.SqlClient" }, - "Middleware": { - "RequestResponseLog": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": true - } - }, - "IPLog": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": true - } - }, - "RecordAccessLogs": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": true - }, - "IgnoreApis": "/api/permission/getnavigationbar,/api/monitor/getids4users,/api/monitor/getaccesslogs,/api/monitor/server,/api/monitor/getactiveusers,/api/monitor/server," - }, - "SignalR": { - "Enabled": false - }, - "SignalRSendLog": { - "Enabled": false - }, - "QuartzNetJob": { - "Enabled": true - }, - "Consul": { - "Enabled": false - }, - "IpRateLimit": { - "Enabled": true - } + { + "ConnId": "WMBLOG_MSSQL_2", + "DBType": 1, + "Enabled": false, + "HitRate": 30, + "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_2;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", + "ProviderName": "System.Data.SqlClient" }, - "IpRateLimiting": { - "EnableEndpointRateLimiting": true, //False: globally executed, true: executed for each - "StackBlockedRequests": false, //False: Number of rejections should be recorded on another counter - "RealIpHeader": "X-Real-IP", - "ClientIdHeader": "X-ClientId", - "IpWhitelist": [], //白名单 - "EndpointWhitelist": [ "get:/api/xxx", "*:/api/yyy" ], - "ClientWhitelist": [ "dev-client-1", "dev-client-2" ], - "QuotaExceededResponse": { - "Content": "{{\"status\":429,\"msg\":\"访问过于频繁,请稍后重试\",\"success\":false}}", - "ContentType": "application/json", - "StatusCode": 429 - }, - "HttpStatusCode": 429, //返回状态码 - "GeneralRules": [ //api规则,结尾一定要带* - { - "Endpoint": "*:/api/blog*", - "Period": "1m", - "Limit": 20 - }, - { - "Endpoint": "*/api/*", - "Period": "1s", - "Limit": 3 - }, - { - "Endpoint": "*/api/*", - "Period": "1m", - "Limit": 30 - }, - { - "Endpoint": "*/api/*", - "Period": "12h", - "Limit": 500 - } - ] - + { + "ConnId": "WMBLOG_MYSQL", + "DBType": 0, + "Enabled": false, + "HitRate": 20, + "Connection": "server=localhost;Database=blog;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" }, - "ConsulSetting": { - "ServiceName": "BlogCoreService", - "ServiceIP": "localhost", - "ServicePort": "9291", - "ServiceHealthCheck": "/healthcheck", - "ConsulAddress": "http://localhost:8500" + { + "ConnId": "WMBLOG_MYSQL_2", + "DBType": 0, + "Enabled": false, + "HitRate": 20, + "Connection": "server=localhost;Database=blogcore001;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" }, - "PayInfo": { //建行聚合支付信息 - "MERCHANTID": "", //商户号 - "POSID": "", //柜台号 - "BRANCHID": "", //分行号 - "pubKey": "", //公钥 - "USER_ID": "", //操作员号 - "PASSWORD": "", //密码 - "OutAddress": "http://127.0.0.1:12345" //外联地址 + { + "ConnId": "WMBLOG_ORACLE", + "DBType": 3, + "Enabled": false, + "HitRate": 10, + "Connection": "Data Source=127.0.0.1/ops;User ID=OPS;Password=123456;Persist Security Info=True;Connection Timeout=60;" }, - "nacos": { - "ServerAddresses": [ "http://localhost:8848" ], // nacos 连接地址 - "DefaultTimeOut": 15000, // 默认超时时间 - "Namespace": "public", // 命名空间 - "ListenInterval": 10000, // 监听的频率 - "ServiceName": "blog.Core.Api", // 服务名 - "Port": "9291", // 服务端口号 - "RegisterEnabled": true // 是否直接注册nacos + { + "ConnId": "WMBLOG_DM", + "DBType": 5, + "Enabled": false, + "HitRate": 10, + "Connection": "PORT=5236;DATABASE=DAMENG;HOST=localhost;PASSWORD=SYSDBA;USER ID=SYSDBA;" }, - "LogFiedOutPutConfigs": { - "tcpAddressHost": "", // 输出elk的tcp连接地址 - "tcpAddressPort": 0, // 输出elk的tcp端口号 - "ConfigsInfo": [ // 配置的输出elk节点内容 常用语动态标识 - { - "FiedName": "applicationName", - "FiedValue": "Blog.Core.Api" - } - ] + { + "ConnId": "WMBLOG_KDBNDP", + "DBType": 6, + "Enabled": false, + "HitRate": 10, + "Connection": "Server=127.0.0.1;Port=54321;UID=SYSTEM;PWD=system;database=SQLSUGAR4XTEST1;" } + ], + "Audience": { + "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", //不要太短,16位+ + "SecretFile": "C:\\my-file\\blog.core.audience.secret.txt", //安全。内容就是Secret + "Issuer": "Blog.Core", + "Audience": "wr" + }, + "Mongo": { + "ConnectionString": "mongodb://nosql.data", + "Database": "BlogCoreDb" + }, + "Startup": { + "Domain": "http://localhost:9291", + "Cors": { + "PolicyName": "CorsIpAccess", //策略名称 + "EnableAllIPs": false, //当为true时,开放所有IP均可访问。 + // 支持多个域名端口,注意端口号后不要带/斜杆:比如localhost:8000/,是错的 + // 注意,http://127.0.0.1:1818 和 http://localhost:1818 是不一样的 + "IPs": "http://127.0.0.1:2364,http://localhost:2364,http://127.0.0.1:6688,http://localhost:6688" + }, + "AppConfigAlert": { + "Enabled": true + }, + "ApiName": "Blog.Core", + "IdentityServer4": { + "Enabled": false, // 这里默认是false,表示使用jwt,如果设置为true,则表示系统使用Ids4模式 + "AuthorizationUrl": "http://localhost:5004", // 认证中心域名 + "ApiName": "blog.core.api" // 资源服务器 + }, + "Authing": { + "Enabled": false, + "Issuer": "https://uldr24esx31h-demo.authing.cn/oidc", + "Audience": "63d51c4205c2849803be5178", + "JwksUri": "https://uldr24esx31h-demo.authing.cn/oidc/.well-known/jwks.json" + }, + "RedisMq": { + "Enabled": false //redis 消息队列 + }, + "MiniProfiler": { + "Enabled": false //性能分析开启 + }, + "Nacos": { + "Enabled": false //Nacos注册中心 + } + }, + "Middleware": { + "RequestResponseLog": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": true + } + }, + "IPLog": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": true + } + }, + "RecordAccessLogs": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": true + }, + "IgnoreApis": "/api/permission/getnavigationbar,/api/monitor/getids4users,/api/monitor/getaccesslogs,/api/monitor/server,/api/monitor/getactiveusers,/api/monitor/server," + }, + "SignalR": { + "Enabled": false + }, + "SignalRSendLog": { + "Enabled": false + }, + "QuartzNetJob": { + "Enabled": true + }, + "Consul": { + "Enabled": false + }, + "IpRateLimit": { + "Enabled": true + } + }, + "IpRateLimiting": { + "EnableEndpointRateLimiting": true, //False: globally executed, true: executed for each + "StackBlockedRequests": false, //False: Number of rejections should be recorded on another counter + "RealIpHeader": "X-Real-IP", + "ClientIdHeader": "X-ClientId", + "IpWhitelist": [], //白名单 + "EndpointWhitelist": [ "get:/api/xxx", "*:/api/yyy" ], + "ClientWhitelist": [ "dev-client-1", "dev-client-2" ], + "QuotaExceededResponse": { + "Content": "{{\"status\":429,\"msg\":\"访问过于频繁,请稍后重试\",\"success\":false}}", + "ContentType": "application/json", + "StatusCode": 429 + }, + "HttpStatusCode": 429, //返回状态码 + "GeneralRules": [ //api规则,结尾一定要带* + { + "Endpoint": "*:/api/blog*", + "Period": "1m", + "Limit": 20 + }, + { + "Endpoint": "*/api/*", + "Period": "1s", + "Limit": 3 + }, + { + "Endpoint": "*/api/*", + "Period": "1m", + "Limit": 30 + }, + { + "Endpoint": "*/api/*", + "Period": "12h", + "Limit": 500 + } + ] + + }, + "ConsulSetting": { + "ServiceName": "BlogCoreService", + "ServiceIP": "localhost", + "ServicePort": "9291", + "ServiceHealthCheck": "/healthcheck", + "ConsulAddress": "http://localhost:8500" + }, + "PayInfo": { //建行聚合支付信息 + "MERCHANTID": "", //商户号 + "POSID": "", //柜台号 + "BRANCHID": "", //分行号 + "pubKey": "", //公钥 + "USER_ID": "", //操作员号 + "PASSWORD": "", //密码 + "OutAddress": "http://127.0.0.1:12345" //外联地址 + }, + "nacos": { + "ServerAddresses": [ "http://localhost:8848" ], // nacos 连接地址 + "DefaultTimeOut": 15000, // 默认超时时间 + "Namespace": "public", // 命名空间 + "ListenInterval": 10000, // 监听的频率 + "ServiceName": "blog.Core.Api", // 服务名 + "Port": "9291", // 服务端口号 + "RegisterEnabled": true // 是否直接注册nacos + }, + "LogFiedOutPutConfigs": { + "tcpAddressHost": "", // 输出elk的tcp连接地址 + "tcpAddressPort": 0, // 输出elk的tcp端口号 + "ConfigsInfo": [ // 配置的输出elk节点内容 常用语动态标识 + { + "FiedName": "applicationName", + "FiedValue": "Blog.Core.Api" + } + ] + } } diff --git a/Blog.Core.Api/skyapm.json b/Blog.Core.Api/skyapm.json index cd5ed0e..cdb0e60 100644 --- a/Blog.Core.Api/skyapm.json +++ b/Blog.Core.Api/skyapm.json @@ -11,7 +11,7 @@ }, "Logging": { "Level": "Information", - "FilePath": "Log/skyapm-{Date}.log" + "FilePath": "Logs/Skyapm/skyapm-{Date}.log" }, "Transport": { "Interval": 3000, diff --git a/Blog.Core.Common/App.cs b/Blog.Core.Common/App.cs index 008aea5..c2e2e70 100644 --- a/Blog.Core.Common/App.cs +++ b/Blog.Core.Common/App.cs @@ -1,14 +1,24 @@ using Blog.Core.Common.Core; using Blog.Core.Common.HttpContextUser; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; using System; +using System.Linq; namespace Blog.Core.Common; public class App { - public static IServiceProvider RootServices => InternalApp.RootServices ; + public static IServiceProvider RootServices => InternalApp.RootServices; + + /// 获取Web主机环境,如,是否是开发环境,生产环境等 + public static IWebHostEnvironment WebHostEnvironment => InternalApp.WebHostEnvironment; + + /// 获取泛型主机环境,如,是否是开发环境,生产环境等 + public static IHostEnvironment HostEnvironment => InternalApp.HostEnvironment; /// /// 获取请求上下文 @@ -16,4 +26,55 @@ public class App public static HttpContext HttpContext => RootServices?.GetService()?.HttpContext; public static IUser User => HttpContext == null ? null : RootServices?.GetService(); + + /// 解析服务提供器 + /// + /// + public static IServiceProvider GetServiceProvider(Type serviceType, bool mustBuild = false) + { + if (App.HostEnvironment == null || App.RootServices != null && + InternalApp.InternalServices + .Where((u => u.ServiceType == (serviceType.IsGenericType ? serviceType.GetGenericTypeDefinition() : serviceType))) + .Any((u => u.Lifetime == ServiceLifetime.Singleton))) + return App.RootServices; + HttpContext httpContext = App.HttpContext; + if (httpContext?.RequestServices != null) + return httpContext.RequestServices; + if (App.RootServices != null) + { + IServiceScope scope = App.RootServices.CreateScope(); + return scope.ServiceProvider; + } + + if (mustBuild) + { + throw new ApplicationException("当前不可用,必须要等到 WebApplication Build后"); + } + + ServiceProvider serviceProvider = InternalApp.InternalServices.BuildServiceProvider(); + return serviceProvider; + } + + + public static TService GetService(bool mustBuild = true) where TService : class => App.GetService(typeof(TService), null, mustBuild) as TService; + + /// 获取请求生存周期的服务 + /// + /// + /// + /// + public static TService GetService(IServiceProvider serviceProvider, bool mustBuild = true) where TService : class => App.GetService(typeof(TService), serviceProvider, mustBuild) as TService; + + /// 获取请求生存周期的服务 + /// + /// + /// + /// + public static object GetService(Type type, IServiceProvider serviceProvider = null, bool mustBuild = true) => (serviceProvider ?? App.GetServiceProvider(type, mustBuild)).GetService(type); + + public static TOptions GetOptions(IServiceProvider serviceProvider = null) where TOptions : class, new() + { + IOptions service = App.GetService>(serviceProvider ?? App.RootServices, false); + return service?.Value; + } } \ No newline at end of file diff --git a/Blog.Core.Common/Blog.Core.Common.csproj b/Blog.Core.Common/Blog.Core.Common.csproj index fc0abb9..0662bac 100644 --- a/Blog.Core.Common/Blog.Core.Common.csproj +++ b/Blog.Core.Common/Blog.Core.Common.csproj @@ -18,20 +18,23 @@ - + - + + + + + - - - + + @@ -45,6 +48,7 @@ + diff --git a/Blog.Core.Common/Const/SqlSugarConst.cs b/Blog.Core.Common/Const/SqlSugarConst.cs new file mode 100644 index 0000000..f5efd7e --- /dev/null +++ b/Blog.Core.Common/Const/SqlSugarConst.cs @@ -0,0 +1,9 @@ +namespace Blog.Core.Common.Const; + +public class SqlSugarConst +{ + /// + /// 默认Log数据库标识 + /// + public const string LogConfigId = "Log"; +} \ No newline at end of file diff --git a/Blog.Core.Common/Core/InternalApp.cs b/Blog.Core.Common/Core/InternalApp.cs index c1ae8dc..62e0472 100644 --- a/Blog.Core.Common/Core/InternalApp.cs +++ b/Blog.Core.Common/Core/InternalApp.cs @@ -1,17 +1,34 @@ -using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; namespace Blog.Core.Common.Core; public static class InternalApp { + public static IServiceCollection InternalServices; + /// 根服务 public static IServiceProvider RootServices; - public static void ConfigureApplication(this WebApplication app) - { - app.Lifetime.ApplicationStarted.Register(() => { InternalApp.RootServices = app.Services; }); + /// 获取Web主机环境 + public static IWebHostEnvironment WebHostEnvironment; - app.Lifetime.ApplicationStopped.Register(() => { InternalApp.RootServices = null; }); + /// 获取泛型主机环境 + public static IHostEnvironment HostEnvironment; + + public static void ConfigureApplication(this WebApplicationBuilder wab) + { + HostEnvironment = wab.Environment; + WebHostEnvironment = wab.Environment; + InternalServices = wab.Services; + } + + + public static void ConfigureApplication(this IHost app) + { + RootServices = app.Services; } } \ No newline at end of file diff --git a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs index 3d83b00..c3d374a 100644 --- a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs +++ b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs @@ -1,12 +1,40 @@ -using Blog.Core.Model.Models.RootTkey; +using Blog.Core.Common.LogHelper; +using Blog.Core.Model.Models.RootTkey; using Blog.Core.Model.Tenants; using SqlSugar; +using StackExchange.Profiling; using System; +using Serilog; namespace Blog.Core.Common.DB.Aop; public static class SqlSugarAop { + public static void OnLogExecuting(ISqlSugarClient sqlSugarScopeProvider, string sql, SugarParameter[] p, ConnectionConfig config) + { + try + { + MiniProfiler.Current.CustomTiming($"ConnId:[{config.ConfigId}] SQL:", GetParas(p) + "【SQL语句】:" + sql); + + if (!AppSettings.app(new string[] { "AppSettings", "SqlAOP", "Enabled" }).ObjToBool()) return; + + if (AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToConsole", "Enabled" }).ObjToBool() || + AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToFile", "Enabled" }).ObjToBool() || + AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToDB", "Enabled" }).ObjToBool()) + { + using (LogContextExtension.Create.SqlAopPushProperty(sqlSugarScopeProvider)) + { + Log.Information("------------------ \r\n ConnId:[{ConnId}]【SQL语句】: \r\n {Sql}", + config.ConfigId, UtilMethods.GetSqlString(config.DbType, sql, p)); + } + } + } + catch (Exception e) + { + Log.Error("Error occured OnLogExcuting:" + e); + } + } + public static void DataExecuting(object oldValue, DataFilterModel entityInfo) { if (entityInfo.EntityValue is BaseEntity root) diff --git a/Blog.Core.Common/DB/BaseDBConfig.cs b/Blog.Core.Common/DB/BaseDBConfig.cs index 1d86369..d8c3ee5 100644 --- a/Blog.Core.Common/DB/BaseDBConfig.cs +++ b/Blog.Core.Common/DB/BaseDBConfig.cs @@ -12,6 +12,7 @@ namespace Blog.Core.Common.DB * 目前是多库操作,默认加载的是appsettings.json设置为true的第一个db连接。 */ public static (List allDbs, List slaveDbs) MutiConnectionString => MutiInitConn(); + public static ConnectionConfig LogConfig; //日志库 private static string DifDBConnOfSecurity(params string[] conn) { @@ -107,8 +108,6 @@ namespace Blog.Core.Common.DB return mutiDBOperate; } - - } diff --git a/Blog.Core.Common/Helper/RecursionHelper.cs b/Blog.Core.Common/Helper/RecursionHelper.cs index 9b27a37..b4cbd68 100644 --- a/Blog.Core.Common/Helper/RecursionHelper.cs +++ b/Blog.Core.Common/Helper/RecursionHelper.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; namespace Blog.Core.Common.Helper @@ -8,9 +9,8 @@ namespace Blog.Core.Common.Helper /// public static class RecursionHelper { - public static void LoopToAppendChildren(List all, PermissionTree curItem, int pid, bool needbtn) + public static void LoopToAppendChildren(List all, PermissionTree curItem, long pid, bool needbtn) { - var subItems = all.Where(ee => ee.Pid == curItem.value).ToList(); var btnItems = subItems.Where(ss => ss.isbtn == true).ToList(); @@ -28,6 +28,7 @@ namespace Blog.Core.Common.Helper { subItems = subItems.Where(ss => ss.isbtn == false).ToList(); } + if (subItems.Count > 0) { curItem.children = new List(); @@ -49,14 +50,15 @@ namespace Blog.Core.Common.Helper { //subItem.disabled = true;//禁用当前节点 } + LoopToAppendChildren(all, subItem, pid, needbtn); } } + public static void LoopToAppendChildren(List all, DepartmentTree curItem, int pid) { - var subItems = all.Where(ee => ee.Pid == curItem.value).ToList(); - + if (subItems.Count > 0) { curItem.children = new List(); @@ -73,15 +75,14 @@ namespace Blog.Core.Common.Helper { //subItem.disabled = true;//禁用当前节点 } + LoopToAppendChildren(all, subItem, pid); } } - public static void LoopNaviBarAppendChildren(List all, NavigationBar curItem) { - var subItems = all.Where(ee => ee.pid == curItem.id).ToList(); if (subItems.Count > 0) @@ -102,7 +103,6 @@ namespace Blog.Core.Common.Helper } - public static void LoopToAppendChildrenT(List all, T curItem, string parentIdName = "Pid", string idName = "value", string childrenName = "children") { var subItems = all.Where(ee => ee.GetType().GetProperty(parentIdName).GetValue(ee, null).ToString() == curItem.GetType().GetProperty(idName).GetValue(curItem, null).ToString()).ToList(); @@ -113,12 +113,47 @@ namespace Blog.Core.Common.Helper LoopToAppendChildrenT(all, subItem); } } + + /// + /// 将父子级数据结构转换为普通list + /// + /// + /// + public static List TreeToList(List list, Action> action = null) + { + List results = new List(); + foreach (var item in list) + { + results.Add(item); + OperationChildData(results, item, action); + } + + return results; + } + + /// + /// 递归子级数据 + /// + /// 树形列表数据 + /// Item + public static void OperationChildData(List allList, T item, Action> action) + { + dynamic dynItem = item; + if (dynItem.Children == null) return; + if (dynItem.Children.Count <= 0) return; + allList.AddRange(dynItem.Children); + foreach (var subItem in dynItem.Children) + { + action?.Invoke(item, subItem, allList); + OperationChildData(allList, subItem, action); + } + } } public class PermissionTree { - public int value { get; set; } - public int Pid { get; set; } + public long value { get; set; } + public long Pid { get; set; } public string label { get; set; } public int order { get; set; } public bool isbtn { get; set; } @@ -139,8 +174,8 @@ namespace Blog.Core.Common.Helper public class NavigationBar { - public int id { get; set; } - public int pid { get; set; } + public long id { get; set; } + public long pid { get; set; } public int order { get; set; } public string name { get; set; } public bool IsHide { get; set; } = false; @@ -158,15 +193,13 @@ namespace Blog.Core.Common.Helper public bool requireAuth { get; set; } = true; public bool NoTabPage { get; set; } = false; public bool keepAlive { get; set; } = false; - - } public class NavigationBarPro { - public int id { get; set; } - public int parentId { get; set; } + public long id { get; set; } + public long parentId { get; set; } public int order { get; set; } public string name { get; set; } public bool IsHide { get; set; } = false; @@ -184,4 +217,4 @@ namespace Blog.Core.Common.Helper public string icon { get; set; } public bool show { get; set; } = false; } -} +} \ No newline at end of file diff --git a/Blog.Core.Common/HttpPolly/HttpPollyHelper.cs b/Blog.Core.Common/Https/HttpPolly/HttpPollyHelper.cs similarity index 98% rename from Blog.Core.Common/HttpPolly/HttpPollyHelper.cs rename to Blog.Core.Common/Https/HttpPolly/HttpPollyHelper.cs index f1a1e84..1187d71 100644 --- a/Blog.Core.Common/HttpPolly/HttpPollyHelper.cs +++ b/Blog.Core.Common/Https/HttpPolly/HttpPollyHelper.cs @@ -6,7 +6,7 @@ using System.Net.Http; using System.Text; using System.Threading.Tasks; -namespace Blog.Core.Common.HttpPolly +namespace Blog.Core.Common.Https.HttpPolly { public class HttpPollyHelper : IHttpPollyHelper { @@ -35,7 +35,7 @@ namespace Blog.Core.Common.HttpPolly var stringContent = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, stringContent); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { string result = await response.Content.ReadAsStringAsync(); @@ -72,7 +72,7 @@ namespace Blog.Core.Common.HttpPolly var stringContent = new StringContent(request, Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, stringContent); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { string result = await response.Content.ReadAsStringAsync(); @@ -110,7 +110,7 @@ namespace Blog.Core.Common.HttpPolly var stringContent = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, stringContent); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { return await response.Content.ReadAsStringAsync(); @@ -146,7 +146,7 @@ namespace Blog.Core.Common.HttpPolly var stringContent = new StringContent(request, Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, stringContent); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { return await response.Content.ReadAsStringAsync(); @@ -182,7 +182,7 @@ namespace Blog.Core.Common.HttpPolly } var response = await client.GetAsync(url); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { string result = await response.Content.ReadAsStringAsync(); @@ -219,7 +219,7 @@ namespace Blog.Core.Common.HttpPolly } var response = await client.GetAsync(url); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { return await response.Content.ReadAsStringAsync(); ; @@ -256,7 +256,7 @@ namespace Blog.Core.Common.HttpPolly var stringContent = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json"); var response = await client.PutAsync(url, stringContent); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { string result = await response.Content.ReadAsStringAsync(); @@ -294,7 +294,7 @@ namespace Blog.Core.Common.HttpPolly var stringContent = new StringContent(request, Encoding.UTF8, "application/json"); var response = await client.PutAsync(url, stringContent); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { string result = await response.Content.ReadAsStringAsync(); @@ -331,7 +331,7 @@ namespace Blog.Core.Common.HttpPolly } var response = await client.DeleteAsync(url); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { string result = await response.Content.ReadAsStringAsync(); diff --git a/Blog.Core.Common/HttpPolly/IHttpPollyHelper.cs b/Blog.Core.Common/Https/HttpPolly/IHttpPollyHelper.cs similarity index 96% rename from Blog.Core.Common/HttpPolly/IHttpPollyHelper.cs rename to Blog.Core.Common/Https/HttpPolly/IHttpPollyHelper.cs index a3c8b3f..da8bd5a 100644 --- a/Blog.Core.Common/HttpPolly/IHttpPollyHelper.cs +++ b/Blog.Core.Common/Https/HttpPolly/IHttpPollyHelper.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace Blog.Core.Common.HttpPolly +namespace Blog.Core.Common.Https.HttpPolly { public interface IHttpPollyHelper { diff --git a/Blog.Core.Common/Https/RequestIpUtility.cs b/Blog.Core.Common/Https/RequestIpUtility.cs new file mode 100644 index 0000000..ac7e28f --- /dev/null +++ b/Blog.Core.Common/Https/RequestIpUtility.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Microsoft.AspNetCore.Http; + +namespace Blog.Core.Common.Https; + +public static class RequestIpUtility +{ + public static string GetRequestIp(this HttpContext context) + { + string ip = SplitCsv(GetHeaderValueAs(context, "X-Forwarded-For")).FirstOrDefault(); + + if (string.IsNullOrWhiteSpace(ip)) + ip = SplitCsv(GetHeaderValueAs(context, "X-Real-IP")).FirstOrDefault(); + + if (string.IsNullOrWhiteSpace(ip) && context.Connection?.RemoteIpAddress != null) + ip = context.Connection.RemoteIpAddress.ToString(); + + if (string.IsNullOrWhiteSpace(ip)) + ip = GetHeaderValueAs(context, "REMOTE_ADDR"); + + return ip; + } + + public static bool IsLocal(this HttpContext context) + { + return GetRequestIp(context) is "127.0.0.1" or "::1" || context.Request?.IsLocal() == true; + } + + + public static bool IsLocal(this HttpRequest req) + { + var connection = req.HttpContext.Connection; + if (connection.RemoteIpAddress != null) + { + if (connection.LocalIpAddress != null) + { + return connection.RemoteIpAddress.Equals(connection.LocalIpAddress); + } + else + { + return IPAddress.IsLoopback(connection.RemoteIpAddress); + } + } + + // for in memory TestServer or when dealing with default connection info + if (connection.RemoteIpAddress == null && connection.LocalIpAddress == null) + { + return true; + } + + return false; + } + + + private static T GetHeaderValueAs(HttpContext context, string headerName) + { + if (context.Request?.Headers?.TryGetValue(headerName, out var values) ?? false) + { + string rawValues = values.ToString(); + + if (!string.IsNullOrWhiteSpace(rawValues)) + return (T) Convert.ChangeType(values.ToString(), typeof(T)); + } + + return default; + } + + private static List SplitCsv(string csvList) + { + if (string.IsNullOrWhiteSpace(csvList)) + return new List(); + + return csvList + .TrimEnd(',') + .Split(',') + .AsEnumerable() + .Select(s => s.Trim()) + .ToList(); + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Hubs/ChatHub.cs b/Blog.Core.Common/Hubs/ChatHub.cs index ff15c97..1c58c8a 100644 --- a/Blog.Core.Common/Hubs/ChatHub.cs +++ b/Blog.Core.Common/Hubs/ChatHub.cs @@ -83,7 +83,8 @@ namespace Blog.Core.Hubs //2、服务端主动向客户端发送数据,名字千万不能错 if (AppSettings.app(new string[] { "Middleware", "SignalRSendLog", "Enabled" }).ObjToBool()) { - await Clients.All.ReceiveUpdate(LogLock.GetLogData()); + //TODO 主动发送错误消息 + //await Clients.All.ReceiveUpdate(LogLock.GetLogData()); } diff --git a/Blog.Core.Common/LogHelper/LogContextExtension.cs b/Blog.Core.Common/LogHelper/LogContextExtension.cs new file mode 100644 index 0000000..bce80cb --- /dev/null +++ b/Blog.Core.Common/LogHelper/LogContextExtension.cs @@ -0,0 +1,42 @@ +using Serilog.Context; +using SqlSugar; +using System; +using System.Collections.Generic; + +namespace Blog.Core.Common.LogHelper; + +public class LogContextExtension : IDisposable +{ + private readonly Stack _disposableStack = new Stack(); + + public static LogContextExtension Create => new(); + + public void AddStock(IDisposable disposable) + { + _disposableStack.Push(disposable); + } + + public IDisposable SqlAopPushProperty(ISqlSugarClient db) + { + AddStock(LogContext.PushProperty(LogContextStatic.LogSource, LogContextStatic.AopSql)); + AddStock(LogContext.PushProperty(LogContextStatic.SqlOutToConsole, + AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToConsole", "Enabled" }).ObjToBool())); + AddStock(LogContext.PushProperty(LogContextStatic.SqlOutToFile, + AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToFile", "Enabled" }).ObjToBool())); + AddStock(LogContext.PushProperty(LogContextStatic.OutToDb, + AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToDb", "Enabled" }).ObjToBool())); + + AddStock(LogContext.PushProperty(LogContextStatic.SugarActionType, db.SugarActionType)); + + return this; + } + + + public void Dispose() + { + while (_disposableStack.Count > 0) + { + _disposableStack.Pop().Dispose(); + } + } +} \ No newline at end of file diff --git a/Blog.Core.Common/LogHelper/LogContextStatic.cs b/Blog.Core.Common/LogHelper/LogContextStatic.cs new file mode 100644 index 0000000..52a8167 --- /dev/null +++ b/Blog.Core.Common/LogHelper/LogContextStatic.cs @@ -0,0 +1,42 @@ +using System.IO; + +namespace Blog.Core.Common.LogHelper; + +public class LogContextStatic +{ + static LogContextStatic() + { + if (!Directory.Exists(BaseLogs)) + { + Directory.CreateDirectory(BaseLogs); + } + } + + public static readonly string BaseLogs = "Logs"; + public static readonly string BasePathLogs = @"Logs"; + + public static readonly string LogSource = "LogSource"; + public static readonly string AopSql = "AopSql"; + public static readonly string SqlOutToConsole = "OutToConsole"; + public static readonly string SqlOutToFile = "SqlOutToFile"; + public static readonly string OutToDb = "OutToDb"; + public static readonly string SugarActionType = "SugarActionType"; + + public static readonly string FileMessageTemplate = "{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 100); + + + public static string Combine(string path1) + { + return Path.Combine(BaseLogs, path1); + } + + public static string Combine(string path1, string path2) + { + return Path.Combine(BaseLogs, path1, path2); + } + + public static string Combine(string path1, string path2, string path3) + { + return Path.Combine(BaseLogs, path1, path2, path3); + } +} \ No newline at end of file diff --git a/Blog.Core.Common/LogHelper/LogLock.cs b/Blog.Core.Common/LogHelper/LogLock.cs index 70c5f7f..2c9be9a 100644 --- a/Blog.Core.Common/LogHelper/LogLock.cs +++ b/Blog.Core.Common/LogHelper/LogLock.cs @@ -1,5 +1,4 @@ using Blog.Core.Common.Helper; -using log4net; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -7,12 +6,12 @@ using System.IO; using System.Linq; using System.Text; using System.Threading; +using Serilog; namespace Blog.Core.Common.LogHelper { public class LogLock { - private static readonly ILog log = LogManager.GetLogger(typeof(LogLock)); static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim(); static int WritedCount = 0; static int FailedCount = 0; @@ -53,12 +52,14 @@ namespace Blog.Core.Common.LogHelper default: break; } + if (AppSettings.app(new string[] { AppSetingNodeName, AppSetingName, "Enabled" }).ObjToBool()) { if (AppSettings.app(new string[] { AppSetingNodeName, AppSetingName, "LogToDB", "Enabled" }).ObjToBool()) { OutSql2LogToDB(prefix, traceId, dataParas, IsHeader); } + if (AppSettings.app(new string[] { AppSetingNodeName, AppSetingName, "LogToFile", "Enabled" }).ObjToBool()) { OutSql2LogToFile(prefix, traceId, dataParas, IsHeader); @@ -90,6 +91,7 @@ namespace Blog.Core.Common.LogHelper { Directory.CreateDirectory(folderPath); } + //string logFilePath = Path.Combine(path, $@"{filename}.log"); var logFilePath = FileHelper.GetAvailableFileWithPrefixOrderSize(folderPath, prefix); switch (prefix) @@ -98,47 +100,48 @@ namespace Blog.Core.Common.LogHelper AOPLogInfo apiLogAopInfo = JsonConvert.DeserializeObject(dataParas[1]); //记录被拦截方法信息的日志信息 var dataIntercept = "" + - $"【操作时间】:{apiLogAopInfo.RequestTime}\r\n" + - $"【当前操作用户】:{ apiLogAopInfo.OpUserName} \r\n" + - $"【当前执行方法】:{ apiLogAopInfo.RequestMethodName} \r\n" + - $"【携带的参数有】: {apiLogAopInfo.RequestParamsName} \r\n" + - $"【携带的参数JSON】: {apiLogAopInfo.RequestParamsData} \r\n" + - $"【响应时间】:{apiLogAopInfo.ResponseIntervalTime}\r\n" + - $"【执行完成时间】:{apiLogAopInfo.ResponseTime}\r\n" + - $"【执行完成结果】:{apiLogAopInfo.ResponseJsonData}\r\n"; + $"【操作时间】:{apiLogAopInfo.RequestTime}\r\n" + + $"【当前操作用户】:{apiLogAopInfo.OpUserName} \r\n" + + $"【当前执行方法】:{apiLogAopInfo.RequestMethodName} \r\n" + + $"【携带的参数有】: {apiLogAopInfo.RequestParamsName} \r\n" + + $"【携带的参数JSON】: {apiLogAopInfo.RequestParamsData} \r\n" + + $"【响应时间】:{apiLogAopInfo.ResponseIntervalTime}\r\n" + + $"【执行完成时间】:{apiLogAopInfo.ResponseTime}\r\n" + + $"【执行完成结果】:{apiLogAopInfo.ResponseJsonData}\r\n"; dataParas = new string[] { dataIntercept }; break; case "AOPLogEx": AOPLogExInfo apiLogAopExInfo = JsonConvert.DeserializeObject(dataParas[1]); var dataInterceptEx = "" + - $"【操作时间】:{apiLogAopExInfo.ApiLogAopInfo.RequestTime}\r\n" + - $"【当前操作用户】:{ apiLogAopExInfo.ApiLogAopInfo.OpUserName} \r\n" + - $"【当前执行方法】:{ apiLogAopExInfo.ApiLogAopInfo.RequestMethodName} \r\n" + - $"【携带的参数有】: {apiLogAopExInfo.ApiLogAopInfo.RequestParamsName} \r\n" + - $"【携带的参数JSON】: {apiLogAopExInfo.ApiLogAopInfo.RequestParamsData} \r\n" + - $"【响应时间】:{apiLogAopExInfo.ApiLogAopInfo.ResponseIntervalTime}\r\n" + - $"【执行完成时间】:{apiLogAopExInfo.ApiLogAopInfo.ResponseTime}\r\n" + - $"【执行完成结果】:{apiLogAopExInfo.ApiLogAopInfo.ResponseJsonData}\r\n" + - $"【执行完成异常信息】:方法中出现异常:{apiLogAopExInfo.ExMessage}\r\n" + - $"【执行完成异常】:方法中出现异常:{apiLogAopExInfo.InnerException}\r\n"; + $"【操作时间】:{apiLogAopExInfo.ApiLogAopInfo.RequestTime}\r\n" + + $"【当前操作用户】:{apiLogAopExInfo.ApiLogAopInfo.OpUserName} \r\n" + + $"【当前执行方法】:{apiLogAopExInfo.ApiLogAopInfo.RequestMethodName} \r\n" + + $"【携带的参数有】: {apiLogAopExInfo.ApiLogAopInfo.RequestParamsName} \r\n" + + $"【携带的参数JSON】: {apiLogAopExInfo.ApiLogAopInfo.RequestParamsData} \r\n" + + $"【响应时间】:{apiLogAopExInfo.ApiLogAopInfo.ResponseIntervalTime}\r\n" + + $"【执行完成时间】:{apiLogAopExInfo.ApiLogAopInfo.ResponseTime}\r\n" + + $"【执行完成结果】:{apiLogAopExInfo.ApiLogAopInfo.ResponseJsonData}\r\n" + + $"【执行完成异常信息】:方法中出现异常:{apiLogAopExInfo.ExMessage}\r\n" + + $"【执行完成异常】:方法中出现异常:{apiLogAopExInfo.InnerException}\r\n"; dataParas = new string[] { dataInterceptEx }; break; } + var now = DateTime.Now; string logContent = String.Join("\r\n", dataParas); if (IsHeader) { logContent = ( - "--------------------------------\r\n" + - DateTime.Now + "|\r\n" + - String.Join("\r\n", dataParas) + "\r\n" - ); + "--------------------------------\r\n" + + DateTime.Now + "|\r\n" + + String.Join("\r\n", dataParas) + "\r\n" + ); } else { logContent = ( - dataParas[1] + ",\r\n" - ); + dataParas[1] + ",\r\n" + ); } //if (logContent.IsNotEmptyOrNull() && logContent.Length > 500) @@ -148,12 +151,12 @@ namespace Blog.Core.Common.LogHelper if (isWrt) { File.WriteAllText(logFilePath, logContent); - } else { File.AppendAllText(logFilePath, logContent); } + WritedCount++; } catch (Exception e) @@ -170,14 +173,15 @@ namespace Blog.Core.Common.LogHelper LogWriteLock.ExitWriteLock(); } } + public static void OutSql2LogToDB(string prefix, string traceId, string[] dataParas, bool IsHeader = true) { - log4net.LogicalThreadContext.Properties["LogType"] = prefix; - log4net.LogicalThreadContext.Properties["TraceId"] = traceId; - if (dataParas.Length >= 2) - { - log4net.LogicalThreadContext.Properties["DataType"] = dataParas[0]; - } + //log4net.LogicalThreadContext.Properties["LogType"] = prefix; + //log4net.LogicalThreadContext.Properties["TraceId"] = traceId; + //if (dataParas.Length >= 2) + //{ + // log4net.LogicalThreadContext.Properties["DataType"] = dataParas[0]; + //} dataParas = dataParas.Skip(1).ToArray(); @@ -186,32 +190,37 @@ namespace Blog.Core.Common.LogHelper { logContent = (String.Join("", dataParas)); } + switch (prefix) { //DEBUG | INFO | WARN | ERROR | FATAL case "AOPLog": - log.Info(logContent); + //TODO 是否需要输出? + //Log.Information(logContent); break; case "AOPLogEx": - log.Error(logContent); + Log.Error(logContent); break; case "RequestIpInfoLog": - log.Debug(logContent); + //TODO 是否需要Debug输出? + //Log.Debug(logContent); break; case "RecordAccessLogs": - log.Debug(logContent); + //TODO 是否需要Debug输出? + //Log.Debug(logContent); break; case "SqlLog": - log.Info(logContent); + Log.Information(logContent); break; case "RequestResponseLog": - log.Debug(logContent); + //TODO 是否需要Debug输出? + //Log.Debug(logContent); break; default: break; } - } + /// /// 读取文件内容 /// @@ -287,6 +296,7 @@ namespace Blog.Core.Common.LogHelper { LogWriteLock.ExitReadLock(); } + return s; } @@ -315,7 +325,6 @@ namespace Blog.Core.Common.LogHelper } } } - } return requestInfos; @@ -336,16 +345,18 @@ namespace Blog.Core.Common.LogHelper if (!string.IsNullOrEmpty(aoplogContent)) { aopLogs = aoplogContent.Split("--------------------------------") - .Where(d => !string.IsNullOrEmpty(d) && d != "\n" && d != "\r\n") - .Select(d => new LogInfo - { - Datetime = d.Split("|")[0].ObjToDate(), - Content = d.Split("|")[1]?.Replace("\r\n", "
"), - LogColor = "AOP", - }).ToList(); + .Where(d => !string.IsNullOrEmpty(d) && d != "\n" && d != "\r\n") + .Select(d => new LogInfo + { + Datetime = d.Split("|")[0].ObjToDate(), + Content = d.Split("|")[1]?.Replace("\r\n", "
"), + LogColor = "AOP", + }).ToList(); } } - catch (Exception) { } + catch (Exception) + { + } try { @@ -354,17 +365,19 @@ namespace Blog.Core.Common.LogHelper if (!string.IsNullOrEmpty(exclogContent)) { excLogs = exclogContent.Split("--------------------------------") - .Where(d => !string.IsNullOrEmpty(d) && d != "\n" && d != "\r\n") - .Select(d => new LogInfo - { - Datetime = (d.Split("|")[0]).Split(',')[0].ObjToDate(), - Content = d.Split("|")[1]?.Replace("\r\n", "
"), - LogColor = "EXC", - Import = 9, - }).ToList(); + .Where(d => !string.IsNullOrEmpty(d) && d != "\n" && d != "\r\n") + .Select(d => new LogInfo + { + Datetime = (d.Split("|")[0]).Split(',')[0].ObjToDate(), + Content = d.Split("|")[1]?.Replace("\r\n", "
"), + LogColor = "EXC", + Import = 9, + }).ToList(); } } - catch (Exception) { } + catch (Exception) + { + } try @@ -374,16 +387,18 @@ namespace Blog.Core.Common.LogHelper if (!string.IsNullOrEmpty(sqllogContent)) { sqlLogs = sqllogContent.Split("--------------------------------") - .Where(d => !string.IsNullOrEmpty(d) && d != "\n" && d != "\r\n") - .Select(d => new LogInfo - { - Datetime = d.Split("|")[0].ObjToDate(), - Content = d.Split("|")[1]?.Replace("\r\n", "
"), - LogColor = "SQL", - }).ToList(); + .Where(d => !string.IsNullOrEmpty(d) && d != "\n" && d != "\r\n") + .Select(d => new LogInfo + { + Datetime = d.Split("|")[0].ObjToDate(), + Content = d.Split("|")[1]?.Replace("\r\n", "
"), + LogColor = "SQL", + }).ToList(); } } - catch (Exception) { } + catch (Exception) + { + } //try //{ @@ -422,14 +437,17 @@ namespace Blog.Core.Common.LogHelper { aopLogs.AddRange(excLogs); } + if (sqlLogs != null) { aopLogs.AddRange(sqlLogs); } + if (reqresLogs != null) { aopLogs.AddRange(reqresLogs); } + aopLogs = aopLogs.OrderByDescending(d => d.Import).ThenByDescending(d => d.Datetime).Take(100).ToList(); return aopLogs; @@ -450,7 +468,8 @@ namespace Blog.Core.Common.LogHelper Logs = GetRequestInfo(ReadType.Prefix); apiWeeks = (from n in Logs - group n by new { n.Week, n.Url } into g + group n by new { n.Week, n.Url } + into g select new ApiWeek { week = g.Key.Week, @@ -459,7 +478,6 @@ namespace Blog.Core.Common.LogHelper }).ToList(); //apiWeeks = apiWeeks.OrderByDescending(d => d.count).Take(8).ToList(); - } catch (Exception) { @@ -489,10 +507,12 @@ namespace Blog.Core.Common.LogHelper jsonBuilder.Append(item.count); jsonBuilder.Append("\","); } + if (apiweeksCurrentWeek.Count > 0) { jsonBuilder.Remove(jsonBuilder.Length - 1, 1); } + jsonBuilder.Append("},"); } @@ -500,6 +520,7 @@ namespace Blog.Core.Common.LogHelper { jsonBuilder.Remove(jsonBuilder.Length - 1, 1); } + jsonBuilder.Append("]"); //columns.AddRange(apiWeeks.OrderByDescending(d => d.count).Take(8).Select(d => d.url).ToList()); @@ -521,7 +542,8 @@ namespace Blog.Core.Common.LogHelper Logs = GetRequestInfo(ReadType.Prefix); apiDates = (from n in Logs - group n by new { n.Date } into g + group n by new { n.Date } + into g select new ApiDate { date = g.Key.Date, @@ -529,7 +551,6 @@ namespace Blog.Core.Common.LogHelper }).ToList(); apiDates = apiDates.OrderByDescending(d => d.date).Take(7).ToList(); - } catch (Exception) { @@ -552,7 +573,8 @@ namespace Blog.Core.Common.LogHelper apiDates = (from n in Logs where n.Datetime.ObjToDate() >= DateTime.Today - group n by new { hour = n.Datetime.ObjToDate().Hour } into g + group n by new { hour = n.Datetime.ObjToDate().Hour } + into g select new ApiDate { date = g.Key.hour.ToString("00"), @@ -560,7 +582,6 @@ namespace Blog.Core.Common.LogHelper }).ToList(); apiDates = apiDates.OrderBy(d => d.date).Take(24).ToList(); - } catch (Exception) { @@ -580,14 +601,15 @@ namespace Blog.Core.Common.LogHelper /// 精确查找一个 /// Accurate, + /// /// 指定前缀,模糊查找全部 /// Prefix, + /// /// 指定前缀,最新一个文件 /// PrefixLatest } - -} +} \ No newline at end of file diff --git a/Blog.Core.Extensions/Blog.Core.Extensions.csproj b/Blog.Core.Extensions/Blog.Core.Extensions.csproj index 451d24f..9eae3d9 100644 --- a/Blog.Core.Extensions/Blog.Core.Extensions.csproj +++ b/Blog.Core.Extensions/Blog.Core.Extensions.csproj @@ -18,10 +18,10 @@ - + @@ -35,6 +35,7 @@ + diff --git a/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs b/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs index 85d96e9..aed5776 100644 --- a/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs @@ -4,14 +4,13 @@ using System.Threading.Tasks; using Blog.Core.Model; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; +using Serilog; namespace Blog.Core.Extensions.Middlewares { public class ExceptionHandlerMiddleware { private readonly RequestDelegate _next; - private static readonly log4net.ILog Log = - log4net.LogManager.GetLogger(typeof(ExceptionHandlerMiddleware)); public ExceptionHandlerMiddleware(RequestDelegate next) { @@ -48,7 +47,9 @@ namespace Blog.Core.Extensions.Middlewares context.Response.ContentType = "application/json"; - await context.Response.WriteAsync(JsonConvert.SerializeObject((new ApiResponse(StatusCode.CODE500, e.Message)).MessageModel)).ConfigureAwait(false); + await context.Response + .WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE500, e.Message).MessageModel)) + .ConfigureAwait(false); } } -} +} \ No newline at end of file diff --git a/Blog.Core.Extensions/Middlewares/IpLimitMiddleware.cs b/Blog.Core.Extensions/Middlewares/IpLimitMiddleware.cs index 958d6ff..7fe68fc 100644 --- a/Blog.Core.Extensions/Middlewares/IpLimitMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/IpLimitMiddleware.cs @@ -1,8 +1,8 @@ -using System; -using AspNetCoreRateLimit; +using AspNetCoreRateLimit; using Blog.Core.Common; -using log4net; using Microsoft.AspNetCore.Builder; +using System; +using Serilog; namespace Blog.Core.Extensions.Middlewares { @@ -11,7 +11,6 @@ namespace Blog.Core.Extensions.Middlewares /// public static class IpLimitMiddleware { - private static readonly ILog Log = LogManager.GetLogger(typeof(IpLimitMiddleware)); public static void UseIpLimitMiddle(this IApplicationBuilder app) { if (app == null) throw new ArgumentNullException(nameof(app)); diff --git a/Blog.Core.Extensions/Middlewares/IpLogMiddleware.cs b/Blog.Core.Extensions/Middlewares/IpLogMiddleware.cs index b6b91bd..ccd0d7a 100644 --- a/Blog.Core.Extensions/Middlewares/IpLogMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/IpLogMiddleware.cs @@ -1,10 +1,10 @@ -using System; -using System.Threading.Tasks; -using Blog.Core.Common; +using Blog.Core.Common; using Blog.Core.Common.LogHelper; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; +using System; +using System.Threading.Tasks; namespace Blog.Core.Extensions.Middlewares { @@ -19,7 +19,6 @@ namespace Blog.Core.Extensions.Middlewares /// private readonly RequestDelegate _next; private readonly IWebHostEnvironment _environment; - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(IpLogMiddleware)); /// /// diff --git a/Blog.Core.Extensions/Middlewares/MiniProfilerMiddleware.cs b/Blog.Core.Extensions/Middlewares/MiniProfilerMiddleware.cs index 068c4a0..49e3b70 100644 --- a/Blog.Core.Extensions/Middlewares/MiniProfilerMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/MiniProfilerMiddleware.cs @@ -1,7 +1,7 @@ -using System; -using Blog.Core.Common; -using log4net; +using Blog.Core.Common; using Microsoft.AspNetCore.Builder; +using System; +using Serilog; namespace Blog.Core.Extensions.Middlewares { @@ -10,7 +10,6 @@ namespace Blog.Core.Extensions.Middlewares /// public static class MiniProfilerMiddleware { - private static readonly ILog Log = LogManager.GetLogger(typeof(MiniProfilerMiddleware)); public static void UseMiniProfilerMiddleware(this IApplicationBuilder app) { if (app == null) throw new ArgumentNullException(nameof(app)); diff --git a/Blog.Core.Extensions/Middlewares/SignalRSendMiddleware.cs b/Blog.Core.Extensions/Middlewares/SignalRSendMiddleware.cs index 1909c08..f15df7b 100644 --- a/Blog.Core.Extensions/Middlewares/SignalRSendMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/SignalRSendMiddleware.cs @@ -36,6 +36,7 @@ namespace Blog.Core.Extensions.Middlewares { if (AppSettings.app("Middleware", "SignalR", "Enabled").ObjToBool()) { + //TODO 主动发送错误消息 await _hubContext.Clients.All.SendAsync("ReceiveUpdate", LogLock.GetLogData()); } await _next(context); diff --git a/Blog.Core.Extensions/Middlewares/SwaggerMiddleware.cs b/Blog.Core.Extensions/Middlewares/SwaggerMiddleware.cs index 099b257..73af77d 100644 --- a/Blog.Core.Extensions/Middlewares/SwaggerMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/SwaggerMiddleware.cs @@ -1,10 +1,10 @@ -using System; -using System.IO; -using System.Linq; -using Blog.Core.Common; -using log4net; +using Blog.Core.Common; using Microsoft.AspNetCore.Builder; using Swashbuckle.AspNetCore.SwaggerUI; +using System; +using System.IO; +using System.Linq; +using Serilog; using static Blog.Core.Extensions.CustomApiVersion; namespace Blog.Core.Extensions.Middlewares @@ -14,7 +14,6 @@ namespace Blog.Core.Extensions.Middlewares /// public static class SwaggerMiddleware { - private static readonly ILog Log = LogManager.GetLogger(typeof(SwaggerMiddleware)); public static void UseSwaggerMiddle(this IApplicationBuilder app, Func streamHtml) { if (app == null) throw new ArgumentNullException(nameof(app)); @@ -24,10 +23,7 @@ namespace Blog.Core.Extensions.Middlewares { //根据版本名称倒序 遍历展示 var apiName = AppSettings.app(new string[] { "Startup", "ApiName" }); - typeof(ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version => - { - c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{apiName} {version}"); - }); + typeof(ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version => { c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{apiName} {version}"); }); c.SwaggerEndpoint($"https://petstore.swagger.io/v2/swagger.json", $"{apiName} pet"); @@ -38,12 +34,13 @@ namespace Blog.Core.Extensions.Middlewares Log.Error(msg); throw new Exception(msg); } + c.IndexStream = streamHtml; c.DocExpansion(DocExpansion.None); //->修改界面打开时自动折叠 if (Permissions.IsUseIds4) { - c.OAuthClientId("blogadminjs"); + c.OAuthClientId("blogadminjs"); } @@ -52,4 +49,4 @@ namespace Blog.Core.Extensions.Middlewares }); } } -} +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs index 0622e76..0336567 100644 --- a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs @@ -76,10 +76,10 @@ namespace Blog.Core.Extensions var ipLogOpen = AppSettings.app(new string[] { "Middleware", "IPLog", "Enabled" }).ObjToBool(); var recordAccessLogsOpen = AppSettings.app(new string[] { "Middleware", "RecordAccessLogs", "Enabled" }).ObjToBool(); ConsoleHelper.WriteSuccessLine($"OPEN Log: " + - (requestResponseLogOpen ? "RequestResponseLog √," : "") + - (ipLogOpen ? "IPLog √," : "") + - (recordAccessLogsOpen ? "RecordAccessLogs √," : "") - ); + (requestResponseLogOpen ? "RequestResponseLog √," : "") + + (ipLogOpen ? "IPLog √," : "") + + (recordAccessLogsOpen ? "RecordAccessLogs √," : "") + ); // 事务AOP if (!AppSettings.app(new string[] { "AppSettings", "TranAOP", "Enabled" }).ObjToBool()) @@ -213,7 +213,6 @@ namespace Blog.Core.Extensions Console.WriteLine(); } - } public static void AddAppTableConfigSetup(this IServiceCollection services, IHostEnvironment env) @@ -222,7 +221,6 @@ namespace Blog.Core.Extensions if (AppSettings.app(new string[] { "Startup", "AppConfigAlert", "Enabled" }).ObjToBool()) { - if (env.IsDevelopment()) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); @@ -230,6 +228,7 @@ namespace Blog.Core.Extensions } #region 程序配置 + List configInfos = new() { new string[] { "当前环境", Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") }, @@ -238,7 +237,7 @@ namespace Blog.Core.Extensions new string[] { "RabbitMQ消息列队", AppSettings.app("RabbitMQ", "Enabled") }, new string[] { "事件总线(必须开启消息列队)", AppSettings.app("EventBus", "Enabled") }, new string[] { "redis消息队列", AppSettings.app("Startup", "RedisMq", "Enabled") }, - new string[] { "是否多库", AppSettings.app("MutiDBEnabled" ) }, + new string[] { "是否多库", AppSettings.app("MutiDBEnabled") }, new string[] { "读写分离", AppSettings.app("CQRSEnabled") }, }; @@ -253,17 +252,19 @@ namespace Blog.Core.Extensions TableStyle = TableStyle.Alternative }.Writer(ConsoleColor.Blue); Console.WriteLine(); + #endregion 程序配置 #region AOP + List aopInfos = new() -{ + { new string[] { "Redis缓存AOP", AppSettings.app("AppSettings", "RedisCachingAOP", "Enabled") }, new string[] { "内存缓存AOP", AppSettings.app("AppSettings", "MemoryCachingAOP", "Enabled") }, - new string[] { "服务日志AOP", AppSettings.app("AppSettings", "LogAOP", "Enabled" ) }, - new string[] { "事务AOP", AppSettings.app("AppSettings", "TranAOP", "Enabled" ) }, - new string[] { "Sql执行AOP", AppSettings.app("AppSettings", "SqlAOP", "OutToLogFile", "Enabled" ) }, - new string[] { "Sql执行AOP控制台输出", AppSettings.app("AppSettings", "SqlAOP", "OutToConsole", "Enabled" ) }, + new string[] { "服务日志AOP", AppSettings.app("AppSettings", "LogAOP", "Enabled") }, + new string[] { "事务AOP", AppSettings.app("AppSettings", "TranAOP", "Enabled") }, + new string[] { "Sql执行AOP", AppSettings.app("AppSettings", "SqlAOP", "Enabled") }, + new string[] { "Sql执行AOP控制台输出", AppSettings.app("AppSettings", "SqlAOP", "LogToConsole", "Enabled") }, }; new ConsoleTable @@ -277,15 +278,17 @@ namespace Blog.Core.Extensions TableStyle = TableStyle.Alternative }.Writer(ConsoleColor.Blue); Console.WriteLine(); + #endregion AOP #region 中间件 + List MiddlewareInfos = new() { new string[] { "请求纪录中间件", AppSettings.app("Middleware", "RecordAccessLogs", "Enabled") }, - new string[] { "IP记录中间件", AppSettings.app("Middleware", "IPLog", "Enabled" ) }, - new string[] { "请求响应日志中间件", AppSettings.app("Middleware", "RequestResponseLog", "Enabled" ) }, - new string[] { "SingnalR实时发送请求数据中间件", AppSettings.app("Middleware", "SignalR", "Enabled" ) }, + new string[] { "IP记录中间件", AppSettings.app("Middleware", "IPLog", "Enabled") }, + new string[] { "请求响应日志中间件", AppSettings.app("Middleware", "RequestResponseLog", "Enabled") }, + new string[] { "SingnalR实时发送请求数据中间件", AppSettings.app("Middleware", "SignalR", "Enabled") }, new string[] { "IP限流中间件", AppSettings.app("Middleware", "IpRateLimit", "Enabled") }, new string[] { "性能分析中间件", AppSettings.app("Startup", "MiniProfiler", "Enabled") }, new string[] { "Consul注册服务", AppSettings.app("Middleware", "Consul", "Enabled") }, @@ -302,10 +305,9 @@ namespace Blog.Core.Extensions TableStyle = TableStyle.Alternative }.Writer(ConsoleColor.Blue); Console.WriteLine(); + #endregion 中间件 - } - } } -} +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs b/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs index 7f2997b..4351962 100644 --- a/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs +++ b/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs @@ -6,20 +6,18 @@ using Blog.Core.IRepository.Base; using Blog.Core.IServices.BASE; using Blog.Core.Model; using Blog.Core.Repository.Base; +using Blog.Core.Repository.UnitOfWorks; using Blog.Core.Services.BASE; -using log4net; using System; using System.Collections.Generic; using System.IO; using System.Reflection; -using Blog.Core.Repository.UnitOfWorks; +using Serilog; namespace Blog.Core.Extensions { public class AutofacModuleRegister : Autofac.Module { - private static readonly ILog log = LogManager.GetLogger(typeof(AutofacModuleRegister)); - protected override void Load(ContainerBuilder builder) { var basePath = AppContext.BaseDirectory; @@ -34,39 +32,39 @@ namespace Blog.Core.Extensions if (!(File.Exists(servicesDllFile) && File.Exists(repositoryDllFile))) { var msg = "Repository.dll和service.dll 丢失,因为项目解耦了,所以需要先F6编译,再F5运行,请检查 bin 文件夹,并拷贝。"; - log.Error(msg); + Log.Error(msg); throw new Exception(msg); } // AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。 var cacheType = new List(); - if (AppSettings.app(new string[] {"AppSettings", "RedisCachingAOP", "Enabled"}).ObjToBool()) + if (AppSettings.app(new string[] { "AppSettings", "RedisCachingAOP", "Enabled" }).ObjToBool()) { builder.RegisterType(); cacheType.Add(typeof(BlogRedisCacheAOP)); } - if (AppSettings.app(new string[] {"AppSettings", "MemoryCachingAOP", "Enabled"}).ObjToBool()) + if (AppSettings.app(new string[] { "AppSettings", "MemoryCachingAOP", "Enabled" }).ObjToBool()) { builder.RegisterType(); cacheType.Add(typeof(BlogCacheAOP)); } - if (AppSettings.app(new string[] {"AppSettings", "TranAOP", "Enabled"}).ObjToBool()) + if (AppSettings.app(new string[] { "AppSettings", "TranAOP", "Enabled" }).ObjToBool()) { builder.RegisterType(); cacheType.Add(typeof(BlogTranAOP)); } - if (AppSettings.app(new string[] {"AppSettings", "LogAOP", "Enabled"}).ObjToBool()) + if (AppSettings.app(new string[] { "AppSettings", "LogAOP", "Enabled" }).ObjToBool()) { builder.RegisterType(); cacheType.Add(typeof(BlogLogAOP)); } - builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>)).InstancePerDependency();//注册仓储 - builder.RegisterGeneric(typeof(BaseServices<>)).As(typeof(IBaseServices<>)).InstancePerDependency();//注册服务 + builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>)).InstancePerDependency(); //注册仓储 + builder.RegisterGeneric(typeof(BaseServices<>)).As(typeof(IBaseServices<>)).InstancePerDependency(); //注册服务 // 获取 Service.dll 程序集服务,并注册 var assemblysServices = Assembly.LoadFrom(servicesDllFile); diff --git a/Blog.Core.Extensions/ServiceExtensions/HttpPollySetup.cs b/Blog.Core.Extensions/ServiceExtensions/HttpPollySetup.cs index e8e3929..b3147ca 100644 --- a/Blog.Core.Extensions/ServiceExtensions/HttpPollySetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/HttpPollySetup.cs @@ -1,4 +1,4 @@ -using Blog.Core.Common.HttpPolly; +using Blog.Core.Common.Https.HttpPolly; using Blog.Core.Model; using Microsoft.Extensions.DependencyInjection; using Polly; diff --git a/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs new file mode 100644 index 0000000..a112409 --- /dev/null +++ b/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs @@ -0,0 +1,37 @@ +using Blog.Core.Common; +using Blog.Core.Common.LogHelper; +using Blog.Core.Serilog.Extensions; +using Microsoft.Extensions.Hosting; +using Serilog; +using Serilog.Debugging; +using System; +using System.IO; + +namespace Blog.Core.Extensions.ServiceExtensions; + +public static class SerilogSetup +{ + public static IHostBuilder AddSerilogSetup(this IHostBuilder host) + { + if (host == null) throw new ArgumentNullException(nameof(host)); + + var loggerConfiguration = new LoggerConfiguration() + .ReadFrom.Configuration(AppSettings.Configuration) + .Enrich.FromLogContext() + //输出到控制台 + .WriteToConsole() + //将日志保存到文件中 + .WriteToFile(); + //配置日志库 + //.WriteToLogBatching(); + + Log.Logger = loggerConfiguration.CreateLogger(); + + //Serilog 内部日志 + var file = File.CreateText(LogContextStatic.Combine($"SerilogDebug{DateTime.Now:yyyyMMdd}.txt")); + SelfLog.Enable(TextWriter.Synchronized(file)); + + host.UseSerilog(); + return host; + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index 3ecf224..3440b8a 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -1,6 +1,7 @@ using Blog.Core.Common; +using Blog.Core.Common.Const; using Blog.Core.Common.DB; -using Blog.Core.Common.Helper; +using Blog.Core.Common.DB.Aop; using Blog.Core.Common.LogHelper; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; @@ -9,7 +10,6 @@ using StackExchange.Profiling; using System; using System.Collections.Generic; using System.Threading.Tasks; -using Blog.Core.Common.DB.Aop; namespace Blog.Core.Extensions { @@ -48,7 +48,7 @@ namespace Blog.Core.Extensions BaseDBConfig.MutiConnectionString.allDbs.ForEach(m => { - listConfig.Add(new ConnectionConfig() + var config = new ConnectionConfig() { ConfigId = m.ConnId.ObjToString().ToLower(), ConnectionString = m.Connection, @@ -56,29 +56,6 @@ namespace Blog.Core.Extensions IsAutoCloseConnection = true, // Check out more information: https://github.com/anjoy8/Blog.Core/issues/122 //IsShardSameThread = false, - AopEvents = new AopEvents - { - OnLogExecuting = (sql, p) => - { - if (AppSettings.app(new string[] { "AppSettings", "SqlAOP", "Enabled" }).ObjToBool()) - { - if (AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToFile", "Enabled" }).ObjToBool()) - { - Parallel.For(0, 1, e => - { - MiniProfiler.Current.CustomTiming("SQL:", GetParas(p) + "【SQL语句】:" + sql); - //LogLock.OutSql2Log("SqlLog", new string[] { GetParas(p), "【SQL语句】:" + sql }); - LogLock.OutLogAOP("SqlLog", "", new string[] { sql.GetType().ToString(), GetParas(p), "【SQL语句】:" + sql }); - - }); - } - if (AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToConsole", "Enabled" }).ObjToBool()) - { - ConsoleHelper.WriteColorLine(string.Join("\r\n", new string[] { "--------", $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} :" + GetWholeSql(p, sql) }), ConsoleColor.DarkCyan); - } - } - }, - }, MoreSettings = new ConnMoreSettings() { //IsWithNoLockQuery = true, @@ -99,15 +76,29 @@ namespace Blog.Core.Extensions } }, InitKeyType = InitKeyType.Attribute + }; + if (SqlSugarConst.LogConfigId.Equals(m.ConnId)) + { + BaseDBConfig.LogConfig = config; } - ); + + listConfig.Add(config); }); + + if (BaseDBConfig.LogConfig is null) + { + throw new ApplicationException("未配置Log库连接"); + } + return new SqlSugarScope(listConfig, db => { listConfig.ForEach(config => { var dbProvider = db.GetConnectionScope((string)config.ConfigId); + // 打印SQL语句 + dbProvider.Aop.OnLogExecuting = (s, parameters) => SqlSugarAop.OnLogExecuting(dbProvider,s, parameters, config); + // 数据审计 dbProvider.Aop.DataExecuting = SqlSugarAop.DataExecuting; diff --git a/Blog.Core.Extensions/ServiceExtensions/SwaggerSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SwaggerSetup.cs index 449b6a4..85dff62 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SwaggerSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SwaggerSetup.cs @@ -1,7 +1,7 @@ using Blog.Core.Common; -using log4net; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; +using Serilog; using Swashbuckle.AspNetCore.Filters; using System; using System.Collections.Generic; @@ -17,10 +17,6 @@ namespace Blog.Core.Extensions /// public static class SwaggerSetup { - - private static readonly ILog log = - LogManager.GetLogger(typeof(SwaggerSetup)); - public static void AddSwaggerSetup(this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof(services)); @@ -59,7 +55,7 @@ namespace Blog.Core.Extensions } catch (Exception ex) { - log.Error("Blog.Core.xml和Blog.Core.Model.xml 丢失,请检查并拷贝。\n" + ex.Message); + Log.Error("Blog.Core.xml和Blog.Core.Model.xml 丢失,请检查并拷贝。\n" + ex.Message); } // 开启加权小锁 @@ -82,12 +78,13 @@ namespace Blog.Core.Extensions Implicit = new OpenApiOAuthFlow { AuthorizationUrl = new Uri($"{AppSettings.app(new string[] { "Startup", "IdentityServer4", "AuthorizationUrl" })}/connect/authorize"), - Scopes = new Dictionary { + Scopes = new Dictionary { - "blog.core.api","ApiResource id" + { + "blog.core.api", "ApiResource id" + } } } - } } }); } @@ -97,14 +94,11 @@ namespace Blog.Core.Extensions c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"", - Name = "Authorization",//jwt默认的参数名称 - In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中) + Name = "Authorization", //jwt默认的参数名称 + In = ParameterLocation.Header, //jwt默认存放Authorization信息的位置(请求头中) Type = SecuritySchemeType.ApiKey }); } - - - }); services.AddSwaggerGenNewtonsoftSupport(); } @@ -124,11 +118,11 @@ namespace Blog.Core.Extensions /// V1 版本 /// V1 = 1, + /// /// V2 版本 /// V2 = 2, } } - -} +} \ No newline at end of file diff --git a/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj b/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj index 5cf8202..398aab4 100644 --- a/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj +++ b/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj @@ -11,7 +11,7 @@ - +
diff --git a/Blog.Core.Serilog/Blog.Core.Serilog.csproj b/Blog.Core.Serilog/Blog.Core.Serilog.csproj new file mode 100644 index 0000000..07fa26f --- /dev/null +++ b/Blog.Core.Serilog/Blog.Core.Serilog.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/Blog.Core.Serilog/Extensions/LoggerConfigurationExtensions.cs b/Blog.Core.Serilog/Extensions/LoggerConfigurationExtensions.cs new file mode 100644 index 0000000..2736aa1 --- /dev/null +++ b/Blog.Core.Serilog/Extensions/LoggerConfigurationExtensions.cs @@ -0,0 +1,121 @@ +using Blog.Core.Common; +using Blog.Core.Common.LogHelper; +using Serilog; +using Serilog.Events; +using Serilog.Filters; +using SqlSugar; + +namespace Blog.Core.Serilog.Extensions; + +public static class LoggerConfigurationExtensions +{ + public static LoggerConfiguration WriteToSqlServer(this LoggerConfiguration loggerConfiguration) + { + var logConnectionStrings = AppSettings.app("LogConnectionStrings"); + if (logConnectionStrings.IsNullOrEmpty()) return loggerConfiguration; + + //输出SQL + //loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + // lg.FilterSqlLog().WriteTo.MSSqlServer(logConnectionStrings, new MSSqlServerSinkOptions() + // { + // TableName = "SqlLog", + // AutoCreateSqlTable = true + // })); + + //输出普通日志 + //loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + // lg.FilterRemoveSqlLog().Filter.ByIncludingOnly(p => p.Level >= LogEventLevel.Error) + // .WriteTo.MSSqlServer(logConnectionStrings, new MSSqlServerSinkOptions() + // { + // TableName = "ErrorLog", + // AutoCreateSqlTable = true + // })); + //loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + // lg.FilterRemoveSqlLog().Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Warning) + // .WriteTo.MSSqlServer(logConnectionStrings, new MSSqlServerSinkOptions() + // { + // TableName = "WarningLog", + // AutoCreateSqlTable = true + // })); + //loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + // lg.FilterRemoveSqlLog().Filter.ByIncludingOnly(p => p.Level <= LogEventLevel.Information) + // .WriteTo.MSSqlServer(logConnectionStrings, new MSSqlServerSinkOptions() + // { + // TableName = "InformationLog", + // AutoCreateSqlTable = true + // })); + + return loggerConfiguration; + } + + public static LoggerConfiguration WriteToConsole(this LoggerConfiguration loggerConfiguration) + { + //输出普通日志 + loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + lg.FilterRemoveSqlLog().WriteTo.Console()); + + //输出SQL + loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + lg.FilterSqlLog().Filter.ByIncludingOnly(Matching.WithProperty(LogContextStatic.SqlOutToConsole, s => s)) + .WriteTo.Console()); + + return loggerConfiguration; + } + + public static LoggerConfiguration WriteToFile(this LoggerConfiguration loggerConfiguration) + { + //输出SQL + loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + lg.FilterSqlLog().Filter.ByIncludingOnly(Matching.WithProperty(LogContextStatic.SqlOutToFile, s => s)) + .WriteTo.Async(s => s.File(LogContextStatic.Combine(LogContextStatic.AopSql, @"AopSql.txt"), rollingInterval: RollingInterval.Day, + outputTemplate: LogContextStatic.FileMessageTemplate, retainedFileCountLimit: 31))); + //输出普通日志 + loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + lg.FilterRemoveSqlLog().WriteTo.Async(s => s.File(LogContextStatic.Combine(LogContextStatic.BasePathLogs, @"Log.txt"), rollingInterval: RollingInterval.Day, + outputTemplate: LogContextStatic.FileMessageTemplate, retainedFileCountLimit: 31))); + return loggerConfiguration; + } + + public static LoggerConfiguration FilterSqlLog(this LoggerConfiguration lc) + { + lc = lc.Filter.ByIncludingOnly(Matching.WithProperty(LogContextStatic.LogSource, s => LogContextStatic.AopSql.Equals(s))); + return lc; + } + + public static IEnumerable FilterSqlLog(this IEnumerable batch) + { + return batch.Where(s => s.WithProperty(LogContextStatic.LogSource, q => LogContextStatic.AopSql.Equals(q))) + .Where(s => s.WithProperty(LogContextStatic.SugarActionType, + q => !new[] { SugarActionType.UnKnown, SugarActionType.Query }.Contains(q))); + } + + public static LoggerConfiguration FilterRemoveSqlLog(this LoggerConfiguration lc) + { + lc = lc.Filter.ByIncludingOnly(WithProperty(LogContextStatic.LogSource, s => !LogContextStatic.AopSql.Equals(s))); + return lc; + } + + public static IEnumerable FilterRemoveOtherLog(this IEnumerable batch) + { + return batch.Where(s => WithProperty(LogContextStatic.LogSource, + q => !LogContextStatic.AopSql.Equals(q))(s)); + } + + public static Func WithProperty(string propertyName, Func predicate) + { + //如果不包含属性 也认为是true + return e => + { + if (!e.Properties.TryGetValue(propertyName, out var propertyValue)) return true; + + return propertyValue is ScalarValue { Value: T value } && predicate(value); + }; + } + + public static bool WithProperty(this LogEvent e, string key, Func predicate) + { + if (!e.Properties.TryGetValue(key, out var propertyValue)) return false; + + return propertyValue is ScalarValue { Value: T value } && predicate(value); + } +} \ No newline at end of file diff --git a/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs b/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs new file mode 100644 index 0000000..cab7ae5 --- /dev/null +++ b/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Http; +using Serilog.Events; + +namespace Blog.Core.Serilog.Utility; + +public class SerilogRequestUtility +{ + private static readonly List _ignoreUrl = new() + { + "/job", + }; + + private static LogEventLevel DefaultGetLevel( + HttpContext ctx, + double _, + Exception? ex) + { + return ex is null && ctx.Response.StatusCode <= 499 ? LogEventLevel.Information : LogEventLevel.Error; + } + + public static LogEventLevel GetRequestLevel(HttpContext ctx, double _, Exception? ex) => + ex is null && ctx.Response.StatusCode <= 499 ? IgnoreRequest(ctx) : LogEventLevel.Error; + + private static LogEventLevel IgnoreRequest(HttpContext ctx) + { + var path = ctx.Request.Path.Value; + if (path.IsNullOrEmpty()) + { + return LogEventLevel.Information; + } + + return _ignoreUrl.Any(s => path.StartsWith(s)) ? LogEventLevel.Verbose : LogEventLevel.Information; + } +} \ No newline at end of file diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs index 1dcc57e..1d501c3 100644 --- a/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs @@ -34,7 +34,7 @@ namespace Blog.Core.Tasks } public async Task Run(IJobExecutionContext context) { - + // 可以直接获取 JobDetail 的值 var jobKey = context.JobDetail.Key; var jobId = jobKey.Name; @@ -94,7 +94,7 @@ namespace Blog.Core.Tasks Parallel.For(0, 1, e => { - LogLock.OutLogAOP("ACCESSTRENDLOG","",new string[] { activeUserVMs.GetType().ToString(), JsonConvert.SerializeObject(activeUserVMs) }, false); + LogLock.OutLogAOP("ACCESSTRENDLOG", "", new string[] { activeUserVMs.GetType().ToString(), JsonConvert.SerializeObject(activeUserVMs) }, false); }); } diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_OperateLog_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_OperateLog_Quartz.cs index 18c4c29..abcd81e 100644 --- a/Blog.Core.Tasks/QuartzNet/Jobs/Job_OperateLog_Quartz.cs +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_OperateLog_Quartz.cs @@ -17,19 +17,21 @@ namespace Blog.Core.Tasks { public class Job_OperateLog_Quartz : JobBase, IJob { - private readonly IOperateLogServices _operateLogServices; + private readonly IOperateLogServices _operateLogServices; private readonly IWebHostEnvironment _environment; - public Job_OperateLog_Quartz(IOperateLogServices operateLogServices,IWebHostEnvironment environment, ITasksQzServices tasksQzServices,ITasksLogServices tasksLogServices) - :base(tasksQzServices, tasksLogServices) + public Job_OperateLog_Quartz(IOperateLogServices operateLogServices, IWebHostEnvironment environment, ITasksQzServices tasksQzServices, ITasksLogServices tasksLogServices) + : base(tasksQzServices, tasksLogServices) { - _operateLogServices = operateLogServices; - _environment = environment; + _operateLogServices = operateLogServices; + _environment = environment; } + public async Task Execute(IJobExecutionContext context) { var executeLog = await ExecuteJob(context, async () => await Run(context)); } + public async Task Run(IJobExecutionContext context) { @@ -78,7 +80,4 @@ namespace Blog.Core.Tasks } } } - - - -} +} \ No newline at end of file diff --git a/Blog.Core.Tests/DependencyInjection/DI_Test.cs b/Blog.Core.Tests/DependencyInjection/DI_Test.cs index ff2c74c..d425fa0 100644 --- a/Blog.Core.Tests/DependencyInjection/DI_Test.cs +++ b/Blog.Core.Tests/DependencyInjection/DI_Test.cs @@ -59,7 +59,6 @@ namespace Blog.Core.Tests services.AddAutoMapper(typeof(Startup)); services.AddSingleton(new AppSettings(basePath)); - services.AddSingleton(new LogLock(basePath)); services.AddScoped(); services.AddScoped(); diff --git a/Blog.Core.sln b/Blog.Core.sln index c8f6150..bf4f65c 100644 --- a/Blog.Core.sln +++ b/Blog.Core.sln @@ -57,6 +57,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.Serilog.Es", "Blo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Nacos", "Ocelot.Provider.Nacos\Ocelot.Provider.Nacos.csproj", "{6463FB13-5F01-4A1D-8B62-A454FB3812EB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blog.Core.Serilog", "Blog.Core.Serilog\Blog.Core.Serilog.csproj", "{7F9057F0-ED8D-4694-B590-7D75C012DF00}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -119,6 +121,10 @@ Global {6463FB13-5F01-4A1D-8B62-A454FB3812EB}.Debug|Any CPU.Build.0 = Debug|Any CPU {6463FB13-5F01-4A1D-8B62-A454FB3812EB}.Release|Any CPU.ActiveCfg = Release|Any CPU {6463FB13-5F01-4A1D-8B62-A454FB3812EB}.Release|Any CPU.Build.0 = Release|Any CPU + {7F9057F0-ED8D-4694-B590-7D75C012DF00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F9057F0-ED8D-4694-B590-7D75C012DF00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F9057F0-ED8D-4694-B590-7D75C012DF00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F9057F0-ED8D-4694-B590-7D75C012DF00}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE