diff --git a/Core/MAX.Framework/DependencyInjection/LoggerExtensions.cs b/Core/MAX.Framework/DependencyInjection/LoggerExtensions.cs
index 500f7dc..b4be8e1 100644
--- a/Core/MAX.Framework/DependencyInjection/LoggerExtensions.cs
+++ b/Core/MAX.Framework/DependencyInjection/LoggerExtensions.cs
@@ -1,22 +1,75 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
-using NLog.Web;
-using System;
-using System.Collections.Generic;
-using System.Text;
+using Serilog;
+using Serilog.Debugging;
namespace MAX.Framework.DependencyInjection
{
+ ///
+ /// Serilog 日志接入:读取 appsettings.Serilog.json(文件缺失时回退到主配置里的 Serilog 节点),
+ /// 再把 Serilog 接入 ASP.NET Core 日志管线,使 ILogger<T> 与 Log 静态入口共用同一套 Sink。
+ /// 日志按等级分目录(logs/等级/日期.log)由 appsettings.Serilog.json 里的 File sink + rollingInterval 负责。
+ ///
public static class LoggerExtensions
{
- public static WebApplicationBuilder AddLogger(this WebApplicationBuilder builder)
+ /// Serilog 独立配置文件(构建/发布会复制到输出目录)。
+ private const string ConfigurationFileName = "appsettings.Serilog.json";
+
+ public static IHostApplicationBuilder AddLogger(this IHostApplicationBuilder host)
{
- builder.Logging.ClearProviders();
- //builder.Logging.SetMinimumLevel(LogLevel.Information);
- builder.Host.UseNLog();
- return builder;
+ IConfigurationManager configuration = host.Configuration;
+
+ // Serilog 默认会静默吞掉配置错误(Sink 名写错、过滤器语法错误等),
+ // 开发环境把自诊断信息输出到控制台,避免再出现「配置了却没有日志」却查不到原因。
+ if (host.Environment.IsDevelopment())
+ {
+ SelfLog.Enable(Console.Error.WriteLine);
+ }
+
+ string? configurationPath = ResolveConfigurationPath(host);
+ if (configurationPath is null)
+ {
+ // 不直接抛异常:允许把 Serilog 节点写进 appsettings.json,但必须显式提示,而不是静默降级成「无任何 Sink」。
+ Console.Error.WriteLine($"[Serilog] 未找到 {ConfigurationFileName},将使用主配置中的 Serilog 节点。");
+ }
+ else
+ {
+ configuration.AddJsonFile(configurationPath, optional: false, reloadOnChange: true);
+ }
+
+ Log.Logger = new LoggerConfiguration()
+ .ReadFrom.Configuration(configuration)
+ .Enrich.FromLogContext()
+ .WriteTo.Console()
+ .CreateLogger();
+
+ // 接管 ASP.NET Core 日志管线:宿主日志与 ILogger 统一写入 Log.Logger;
+ // dispose: true 让宿主停止时自动 CloseAndFlush,保证缓冲中的日志落盘。
+ host.Services.AddSerilog(Log.Logger, dispose: true);
+
+ return host;
+ }
+
+ ///
+ /// 定位 Serilog 配置文件:支持绝对路径,其余情况依次在内容根目录(开发时即项目目录)与程序输出目录中查找。
+ ///
+ private static string? ResolveConfigurationPath(IHostApplicationBuilder host)
+ {
+ if (Path.IsPathRooted(ConfigurationFileName))
+ {
+ return File.Exists(ConfigurationFileName) ? ConfigurationFileName : null;
+ }
+
+ string[] candidates =
+ [
+ Path.Combine(host.Environment.ContentRootPath, ConfigurationFileName),
+ Path.Combine(AppContext.BaseDirectory, ConfigurationFileName),
+ ];
+
+ return candidates.FirstOrDefault(File.Exists);
}
}
}
diff --git a/Core/MAX.Framework/DependencyInjection/ServiceCollectionExtensions.cs b/Core/MAX.Framework/DependencyInjection/ServiceCollectionExtensions.cs
index e5262de..6fa5a36 100644
--- a/Core/MAX.Framework/DependencyInjection/ServiceCollectionExtensions.cs
+++ b/Core/MAX.Framework/DependencyInjection/ServiceCollectionExtensions.cs
@@ -33,6 +33,7 @@ namespace MAX.Framework.DependencyInjection
///
public static IServiceCollection AddStore(this IHostApplicationBuilder host)
{
+ host.AddLogger();
//host.Services.AddSingleton(NLogger.Instance);//注册log
IEnumerable assemblies = GetApplicationAssemblies();
diff --git a/Core/MAX.Framework/Logger/NLogger.cs b/Core/MAX.Framework/Logger/NLogger.cs
deleted file mode 100644
index 6b287d9..0000000
--- a/Core/MAX.Framework/Logger/NLogger.cs
+++ /dev/null
@@ -1,138 +0,0 @@
-//using MAX.Core.Interfaces;
-//using MAX.Core.Logger;
-//using NLog;
-//using System;
-//using LLogLevel = MAX.Core.Logger.LogLevel;
-//using NLogLevel = NLog.LogLevel;
-
-//namespace MAX.Framework.Logger;
-
-//public class NLogger : SingleInstance, ILoggerFacade
-//{
-// private readonly ILogger _log;
-
-// private NLogger()
-// {
-// _log = LogManager.GetLogger("GlobalLogger");
-// }
-
-// public bool IsDebugEnabled { get; private set; } = true;
-
-// public void Debug(Exception ex, string message)
-// {
-// Execute(NLogLevel.Debug, ex, message);
-// }
-
-// public void Debug(string message)
-// {
-// Execute(NLogLevel.Debug, message);
-// }
-// public void Error(Exception ex)
-// {
-// Execute(NLogLevel.Error, ex);
-// }
-// public void Error(Exception ex, string message)
-// {
-// Execute(NLogLevel.Error, ex, message);
-// }
-
-// public void Error(string message)
-// {
-// Execute(NLogLevel.Error, message);
-// }
-
-// public void Fatal(Exception ex, string message)
-// {
-// Execute(NLogLevel.Fatal, ex, message);
-// }
-
-// public void Fatal(string message)
-// {
-// Execute(NLogLevel.Fatal, message);
-// }
-
-// public void Info(Exception ex, string message)
-// {
-// Execute(NLogLevel.Info, ex, message);
-// }
-
-// public void Info(string message)
-// {
-// Execute(NLogLevel.Info, message);
-// }
-
-// public void Warn(Exception ex, string message)
-// {
-// Execute(NLogLevel.Warn, ex, message);
-// }
-
-// public void Warn(string message)
-// {
-// Execute(NLogLevel.Warn, message);
-// }
-// public void Log(string message, LLogLevel level)
-// {
-// NLogLevel lv = NLogLevel.Info;
-// switch (level)
-// {
-// case LLogLevel.Trace:
-// lv = NLogLevel.Trace;
-// break;
-// case LLogLevel.Debug:
-// lv = NLogLevel.Debug;
-// break;
-// case LLogLevel.Info:
-// lv = NLogLevel.Info;
-// break;
-// case LLogLevel.Warn:
-// lv = NLogLevel.Warn;
-// break;
-// case LLogLevel.Error:
-// lv = NLogLevel.Error;
-// break;
-// case LLogLevel.Fatal:
-// lv = NLogLevel.Fatal;
-// break;
-// case LLogLevel.None:
-// lv = NLogLevel.Off;
-// break;
-// }
-// Execute(lv, message);
-// }
-// ///
-// /// 是否启用
-// ///
-// private bool Enabled(NLogLevel level)
-// {
-// if (level > NLogLevel.Debug)
-// {
-// return true;
-// }
-
-// return IsDebugEnabled;
-// }
-
-// ///
-// /// 执行
-// ///
-// private void Execute(NLogLevel level, Exception ex, string message = "")
-// {
-// if (Enabled(level))
-// {
-// _log.Log(level, ex, message, null);
-// }
-// }
-
-
-// ///
-// /// 执行
-// ///
-// private void Execute(NLogLevel level, string message)
-// {
-// if (Enabled(level))
-// {
-// _log.Log(level, message);
-// }
-// }
-
-//}
diff --git a/Core/MAX.Framework/MAX.Framework.csproj b/Core/MAX.Framework/MAX.Framework.csproj
index ccff2f7..504d8c4 100644
--- a/Core/MAX.Framework/MAX.Framework.csproj
+++ b/Core/MAX.Framework/MAX.Framework.csproj
@@ -16,9 +16,10 @@
-
+
+
@@ -31,4 +32,10 @@
+
+
+ PreserveNewest
+
+
+
diff --git a/Core/MAX.Framework/appsettings.Serilog.json b/Core/MAX.Framework/appsettings.Serilog.json
new file mode 100644
index 0000000..996c119
--- /dev/null
+++ b/Core/MAX.Framework/appsettings.Serilog.json
@@ -0,0 +1,141 @@
+{
+ "Serilog": {
+ "Using": [
+ "Serilog.Sinks.Console",
+ "Serilog.Sinks.File",
+ "Serilog.Filters.Expressions"
+ ],
+ "MinimumLevel": {
+ "Default": "Debug",
+ "Override": {
+ "Microsoft": "Information",
+ "Microsoft.AspNetCore": "Warning",
+ "System": "Warning",
+ "Hangfire": "Information"
+ }
+ },
+ "Enrich": [
+ "FromLogContext"
+ ],
+ "Properties": {
+ "Application": "MyAspNetCore"
+ },
+ "WriteTo": [
+ {
+ "Name": "Logger",
+ "Args": {
+ "configureLogger": {
+ "Filter": [
+ { "Name": "ByIncludingOnly", "Args": { "expression": "@Level = 'Debug'" } }
+ ],
+ "WriteTo": [
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Debug/.log",
+ "rollingInterval": "Day",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
+ "retainedFileCountLimit": 365,
+ "fileSizeLimitBytes": 104857600,
+ "rollOnFileSizeLimit": true
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "Name": "Logger",
+ "Args": {
+ "configureLogger": {
+ "Filter": [
+ { "Name": "ByIncludingOnly", "Args": { "expression": "@Level = 'Information'" } }
+ ],
+ "WriteTo": [
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Info/.log",
+ "rollingInterval": "Day",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
+ "retainedFileCountLimit": 365,
+ "fileSizeLimitBytes": 104857600,
+ "rollOnFileSizeLimit": true
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "Name": "Logger",
+ "Args": {
+ "configureLogger": {
+ "Filter": [
+ { "Name": "ByIncludingOnly", "Args": { "expression": "@Level = 'Warning'" } }
+ ],
+ "WriteTo": [
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Warning/.log",
+ "rollingInterval": "Day",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
+ "retainedFileCountLimit": 365,
+ "fileSizeLimitBytes": 104857600,
+ "rollOnFileSizeLimit": true
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "Name": "Logger",
+ "Args": {
+ "configureLogger": {
+ "Filter": [
+ { "Name": "ByIncludingOnly", "Args": { "expression": "@Level = 'Error'" } }
+ ],
+ "WriteTo": [
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Error/.log",
+ "rollingInterval": "Day",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
+ "retainedFileCountLimit": 365,
+ "fileSizeLimitBytes": 104857600,
+ "rollOnFileSizeLimit": true
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "Name": "Logger",
+ "Args": {
+ "configureLogger": {
+ "Filter": [
+ { "Name": "ByIncludingOnly", "Args": { "expression": "@Level = 'Fatal'" } }
+ ],
+ "WriteTo": [
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Fatal/.log",
+ "rollingInterval": "Day",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
+ "retainedFileCountLimit": 365,
+ "fileSizeLimitBytes": 104857600,
+ "rollOnFileSizeLimit": true
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/Modules/CloudProduct/Store.CloudProduct.csproj b/Modules/CloudProduct/Store.CloudProduct.csproj
index c8632f9..453bd03 100644
--- a/Modules/CloudProduct/Store.CloudProduct.csproj
+++ b/Modules/CloudProduct/Store.CloudProduct.csproj
@@ -10,12 +10,18 @@
-
-
+ 由 builder.AddStoreHub() 自动并入宿主配置树(在 appsettings*.json 之后、环境变量之前)。 -->
+
+
+
+ PreserveNewest
+
+
diff --git a/Modules/CloudProduct/appsettings.json b/Modules/CloudProduct/appsettings.CloudProduct.json
similarity index 100%
rename from Modules/CloudProduct/appsettings.json
rename to Modules/CloudProduct/appsettings.CloudProduct.json
diff --git a/Store.API/Controllers/AuthController.cs b/Store.API/Controllers/AuthController.cs
index d51369a..973d759 100644
--- a/Store.API/Controllers/AuthController.cs
+++ b/Store.API/Controllers/AuthController.cs
@@ -16,13 +16,15 @@ namespace Store.Api.Controllers
private readonly ITokenService _tokenService;
private readonly ICurrentUser _currentUser;
private readonly IOnlineUserStore _onlineUserStore;
+ private readonly ILogger _logger;
- public AuthController( ITokenService tokenService, ICurrentUser currentUser, IOnlineUserStore onlineUserStore)
+ public AuthController(ITokenService tokenService, ICurrentUser currentUser, IOnlineUserStore onlineUserStore, ILogger logger)
{
//_userService = userService;
_tokenService = tokenService;
_currentUser = currentUser;
_onlineUserStore = onlineUserStore;
+ _logger = logger;
}
///
@@ -32,6 +34,8 @@ namespace Store.Api.Controllers
[AllowAnonymous]
public async Task Login([FromBody] LoginRequest request)
{
+ _logger.LogError("login errr");
+ _logger.LogInformation("login");
if (request is null || string.IsNullOrWhiteSpace(request.UserName) || string.IsNullOrWhiteSpace(request.Password))
{
return ApiResult.Fail("用户名或密码不能为空", 400);
diff --git a/Store.API/Program.cs b/Store.API/Program.cs
index ee73e59..662eaea 100644
--- a/Store.API/Program.cs
+++ b/Store.API/Program.cs
@@ -1,5 +1,4 @@
using Asp.Versioning;
-using MAX.Core.Logger;
using MAX.Framework.DependencyInjection;
using Scalar.AspNetCore;
@@ -10,7 +9,7 @@ public partial class Program
var builder = WebApplication.CreateBuilder(args);
// IOC 注册:自动注册 ISingletonDependency / IScopedDependency / ITransientDependency,并执行所有 IStartup / IModule.AddModule
- // 模块服务注册阶段(等价于 builder.Services.AddStoreHub(builder.Configuration, builder.Environment),额外把配置/环境交给模块)
+ // 模块服务注册阶段(等价于 builder.Services.AddStoreHub(builder.Configuration, builder.Environment),额外把配置/环境交给模块)
builder.AddStore();
builder.Services.AddFreeSqlDB();
// JWT 验证:注册 JwtBearer 认证 + 令牌服务 + OpenAPI Bearer 安全方案(配置节 Jwt)
@@ -37,7 +36,7 @@ public partial class Program
WebApplication app = builder.Build();
-
+
app.UseStore();
// 初始化 KJD 迁移的静态提供者(MysqlUtils 连接提供 / 业务配置读取)
//StoreHub.KJDWebService.Commons.AdoProvider.SetServiceProvider(app.Services);
diff --git a/Store.API/Store.Api.csproj b/Store.API/Store.Api.csproj
index ca96a0d..97aa213 100644
--- a/Store.API/Store.Api.csproj
+++ b/Store.API/Store.Api.csproj
@@ -7,12 +7,7 @@
-
-
-
-
-
-
+
diff --git a/Store.API/dotnet-tools.json b/Store.API/dotnet-tools.json
deleted file mode 100644
index 39007e0..0000000
--- a/Store.API/dotnet-tools.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "version": 1,
- "isRoot": true,
- "tools": {
- "dotnet-ef": {
- "version": "10.0.13",
- "commands": [
- "dotnet-ef"
- ],
- "rollForward": false
- }
- }
-}
\ No newline at end of file