diff --git a/GFramework.Generator/generator/logging/README.md b/GFramework.Generator/generator/logging/README.md new file mode 100644 index 0000000..2ff3880 --- /dev/null +++ b/GFramework.Generator/generator/logging/README.md @@ -0,0 +1,443 @@ +# GFramework 日志代码生成器 (LogAttribute) + +## 概述 + +GFramework 提供了一个强大的日志代码生成器,类似于 Java 的 `@Slf4j` 注解。通过在类上使用 `[Log]` 特性,编译器会自动为该类生成一个日志记录器字段,让您在类的任何地方都能方便地使用日志记录功能。 + +## 快速开始 + +### 1. 基本使用 + +在类上添加 `[Log]` 特性: + +```csharp +using GFramework.Generator.Attributes.generator.logging; + +[Log] +public partial class MyService +{ + public void DoSomething() + { + // 自动生成的 Log 字段可以直接使用 + Log.Info("开始执行操作"); + + try + { + // 业务逻辑 + Log.Debug("执行业务逻辑", new { Operation = "DoSomething" }); + Log.Info("操作执行成功"); + } + catch (Exception ex) + { + Log.Error("操作执行失败", ex); + } + } +} +``` + +编译后,生成器会自动为该类添加: + +```csharp +public partial class MyService +{ + private static ILog Log = Log.CreateLogger("MyService"); +} +``` + +### 2. 自定义类别名称 + +```csharp +[Log("CustomService")] +public partial class MyService +{ + // 生成的 logger 类别为 "CustomService" 而不是 "MyService" +} +``` + +### 3. 自定义字段配置 + +```csharp +[Log(FieldName = "Logger", AccessModifier = "protected", IsStatic = false)] +public partial class MyService +{ + // 生成: protected ILog Logger = Log.CreateLogger("MyService"); +} +``` + +## 特性参数说明 + +### LogAttribute 构造函数参数 + +- **category** (string, 可选): 指定日志类别,默认为类名 + +### 命名参数 + +- **FieldName** (string, 默认 "Log"): 指定生成的字段名称 +- **AccessModifier** (string, 默认 "private"): 指定字段的访问修饰符 +- **IsStatic** (bool, 默认 true): 指定字段是否为静态的 + +## 使用示例 + +### 在系统中的使用 + +```csharp +[Log("System")] +public partial class GameSystem : AbstractSystem +{ + protected override void OnInit() + { + Log.Info("GameSystem 初始化开始"); + + // 初始化逻辑 + Log.Debug("正在加载游戏数据..."); + Log.Info("GameSystem 初始化完成"); + } + + protected override void OnUpdate(float deltaTime) + { + Log.Trace("系统更新", new { DeltaTime = deltaTime }); + } + + protected override void OnDestroy() + { + Log.Info("GameSystem 销毁"); + } +} +``` + +### 在模型中的使用 + +```csharp +[Log("Model")] +public partial class UserModel : AbstractModel +{ + public string UserName { get; set; } + + public void SetUserName(string userName) + { + Log.Debug("设置用户名", new { OldValue = UserName, NewValue = userName }); + UserName = userName; + Log.Info("用户名设置完成", new { UserName = userName }); + } +} +``` + +### 在工具类中的使用 + +```csharp +[Log("Utility")] +public partial class FileUtility +{ + public void SaveFile(string filePath, string content) + { + Log.Debug("开始保存文件", new { FilePath = filePath }); + + try + { + // 文件保存逻辑 + Log.Info("文件保存成功", new { FilePath = filePath }); + } + catch (Exception ex) + { + Log.Error("文件保存失败", ex, new { FilePath = filePath }); + } + } +} +``` + +### 网络服务中的使用 + +```csharp +[Log("Network")] +public partial class NetworkService +{ + public async Task GetDataAsync(string url) + { + Log.Debug("发起网络请求", new { Url = url, Method = "GET" }); + + try + { + var response = await httpClient.GetStringAsync(url); + Log.Info("网络请求成功", new { Url = url, ResponseLength = response.Length }); + return response; + } + catch (Exception ex) + { + Log.Error("网络请求失败", ex, new { Url = url }); + throw; + } + } +} +``` + +### 数据库服务中的使用 + +```csharp +[Log("Database")] +public partial class DatabaseService +{ + public async Task GetUserAsync(int userId) + { + Log.Debug("查询用户信息", new { UserId = userId }); + + try + { + var user = await _dbContext.Users.FindAsync(userId); + if (user != null) + { + Log.Info("用户查询成功", new { UserId = userId, UserName = user.Name }); + } + else + { + Log.Warn("用户不存在", new { UserId = userId }); + } + return user; + } + catch (Exception ex) + { + Log.Error("用户查询失败", ex, new { UserId = userId }); + throw; + } + } +} +``` + +## 高级配置示例 + +### 使用静态字段 + +```csharp +[Log(IsStatic = true)] // 默认配置 +public partial class StaticService +{ + public static void StaticMethod() + { + Log.Info("静态方法调用"); + } +} +``` + +### 使用实例字段 + +```csharp +[Log(IsStatic = false)] +public partial class InstanceService +{ + private readonly string _instanceId; + + public InstanceService(string instanceId) + { + _instanceId = instanceId; + } + + public void InstanceMethod() + { + Log.Info("实例方法调用", new { InstanceId = _instanceId }); + } +} +``` + +### 使用受保护的字段 + +```csharp +[Log(AccessModifier = "protected")] +public partial class BaseService +{ + // 子类可以访问 protected 字段 +} + +[Log] // 派生类也可以有自己的日志记录器 +public partial class DerivedService : BaseService +{ + public void DerivedMethod() + { + Log.Info("派生类方法"); + // 也可以访问基类的 protected Log 字段 + } +} +``` + +## 最佳实践 + +### 1. 使用合适的日志类别名称 + +```csharp +// 好的做法:使用有意义的类别名称 +[Log("UserService")] +public partial class UserService { } + +// 避免:使用过于通用的类别名称 +[Log("Service")] +public partial class UserService { } +``` + +### 2. 合理设置日志级别 + +```csharp +[Log("BusinessLogic")] +public partial class BusinessService +{ + public void ProcessOrder(Order order) + { + // 使用 Info 记录重要的业务流程 + Log.Info("开始处理订单", new { OrderId = order.Id }); + + // 使用 Debug 记录调试信息 + Log.Debug("订单验证通过", new { OrderId = order.Id }); + + // 使用 Warning 记录异常情况 + if (order.Amount > 10000) + { + Log.Warn("大额订单", new { OrderId = order.Id, Amount = order.Amount }); + } + + // 使用 Error 记录错误 + try + { + // 业务逻辑 + } + catch (Exception ex) + { + Log.Error("订单处理失败", ex, new { OrderId = order.Id }); + } + } +} +``` + +### 3. 记录上下文信息 + +```csharp +[Log("UserManagement")] +public partial class UserManager +{ + public void UpdateUserProfile(int userId, UserProfile profile) + { + Log.Info("更新用户资料", new + { + UserId = userId, + Changes = profile.GetChanges(), + Timestamp = DateTime.Now + }); + } +} +``` + +### 4. 在异常处理中使用日志 + +```csharp +[Log("DataAccess")] +public partial class UserRepository +{ + public async Task GetUserAsync(int userId) + { + try + { + return await _context.Users.FirstOrDefaultAsync(u => u.Id == userId); + } + catch (DbException ex) + { + Log.Error("数据库查询失败", ex, new { UserId = userId }); + throw new DataAccessException("无法获取用户信息", ex); + } + } +} +``` + +## 框架组件集成 + +### 在 GFramework 架构中使用 + +```csharp +[Log("Architecture")] +public partial class ExampleArchitecture : AbstractArchitecture +{ + protected override void Init() + { + Log.Info("ExampleArchitecture 初始化开始"); + + // 注册系统 + RegisterSystem(new GameSystem()); + RegisterSystem(new UISystem()); + + // 注册模型 + RegisterModel(new UserModel()); + RegisterModel(new GameModel()); + + // 注册工具 + RegisterUtility(new FileUtility()); + + Log.Info("ExampleArchitecture 初始化完成"); + } +} +``` + +### 在事件处理中使用 + +```csharp +[Log("Event")] +public partial class UserEventHandler +{ + [Log("EventHandler")] + public void OnUserLoginEvent(UserLoginEvent evt) + { + Log.Info("用户登录事件", new + { + UserId = evt.UserId, + LoginTime = evt.LoginTime, + IpAddress = evt.IpAddress + }); + } + + [Log("EventHandler")] + public void OnUserLogoutEvent(UserLogoutEvent evt) + { + Log.Info("用户登出事件", new + { + UserId = evt.UserId, + LogoutTime = evt.LogoutTime, + SessionDuration = evt.SessionDuration + }); + } +} +``` + +## 注意事项 + +1. **部分类**: 使用 `[Log]` 特性的类必须是 `partial` 类,因为生成器会添加字段到同一个类中。 + +2. **命名空间引用**: 确保项目中引用了 `GFramework.Generator.Attributes` 包。 + +3. **编译时生成**: 日志字段是在编译时生成的,不会影响运行时性能。 + +4. **多线程安全**: 生成的 `ILog` 实例是线程安全的,可以在多线程环境中使用。 + +5. **继承**: 派生类可以有自己的 `[Log]` 特性,也可以访问基类的受保护日志字段。 + +## 性能考虑 + +- **编译时生成**: 字段在编译时生成,没有运行时开销 +- **延迟初始化**: 日志记录器按需创建,避免不必要的对象创建 +- **级别检查**: 生成的代码包含级别检查,性能与手动创建的日志记录器相同 + +## 与现有日志系统的兼容性 + +生成的代码完全兼容现有的 `GFramework.Core.logging` 系统: + +```csharp +// 现有的方式仍然可以使用 +var manualLogger = Log.CreateLogger("Manual"); + +// 新生成的字段也可以正常使用 +[Log] +public partial class MyService +{ + public void Method() + { + // 两种方式都可以使用 + manualLogger.Info("手动创建的日志"); + Log.Info("自动生成的日志"); + } +} +``` + +通过使用 `[Log]` 特性,您可以显著减少样板代码,提高开发效率,同时保持日志记录的一致性和灵活性。 \ No newline at end of file