소스 정보
- 저장소
- anybackup-ai/foundationCommon
- 최근 소스 활동
- 2026년 4월 30일 18:05
- 감지된 SKILL.md 언어
- 중국어
- 스타
- 2
- 포크
- 12
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/anybackup-ai/foundationCommon --skill audit-log-reporting명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | audit-log-reporting |
| description | 指导实现审计日志上报功能。当开发人员需要在新模块中添加审计日志上报、创建审计服务层、或集成审计服务RPC客户端时调用此SKILL。 |
本SKILL提供在其他模块/仓库中实现审计日志上报功能的完整指导。审计日志用于记录用户对数据库实例、集群、保护对象等资源的操作行为。
framework_go-thrifts 库(包含 ncaudittype 和 ncauditsvc)service_resource-center_hrc-core 库在实现审计日志功能时,以下仓库可能提供参考和解决方案:
| 仓库 | 用途 | 参考内容 |
|---|---|---|
hrcm-app-dbalone | 参考实现 | service/audit/ 目录下的审计服务层实现 |
hrcm-app-dbcluster | 参考实现 | 集群相关的审计日志上报实现 |
backup-mgm-database | 参考实现 | 保护对象相关的审计日志上报实现 |
i18nlanguageresources | 国际化资源 | event/Audit_event.json 和 resource/Audit_resource.json |
framework_go-thrifts | Thrift类型定义 | ncaudittype 和 ncauditsvc 包 |
service_resource-center_hrc-core | 基础工具 | NewThriftClient 等工具函数 |
遇到问题时的排查建议:
framework_go-thrifts 中的类型定义是否有更新i18nlanguageresources 仓库中的资源码定义service_resource-center_hrc-core 中的客户端创建方式创建 service/audit/ 目录,包含以下文件:
注意:以下常量定义仅供参考格式,请根据实际业务需求定义符合自身业务场景的审计操作类型和资源码。
package audit
// AuditAction 定义审计操作类型常量
type AuditAction string
const (
AuditActionCreated AuditAction = "created"
AuditActionDeleted AuditAction = "deleted"
AuditActionAuthorized AuditAction = "authorized"
AuditActionAuthCancelled AuditAction = "auth_cancelled"
AuditActionFrozen AuditAction = "frozen"
AuditActionThawed AuditAction = "thawed"
AuditActionModified AuditAction = "modified"
)
// AuditResCode 定义审计资源码常量
type AuditResCode string
const (
AuditResDBInstanceCreated AuditResCode = "Audit.DBInstanceCreated"
AuditResDBInstanceDeleted AuditResCode = "Audit.DBInstanceDeleted"
AuditResDBInstanceAuthorized AuditResCode = "Audit.DBInstanceAuthorized"
AuditResDBInstanceAuthCancelled AuditResCode = "Audit.DBInstanceAuthCancelled"
AuditResDBInstanceFrozen AuditResCode = "Audit.DBInstanceFrozen"
AuditResDBInstanceThawed AuditResCode = "Audit.DBInstanceThawed"
AuditResDBInstanceModified AuditResCode = "Audit.DBInstanceModified"
)
// AuditActionToResCode 将审计Action映射到资源码
var AuditActionToResCode = map[AuditAction]AuditResCode{
AuditActionCreated: AuditResDBInstanceCreated,
AuditActionDeleted: AuditResDBInstanceDeleted,
AuditActionAuthorized: AuditResDBInstanceAuthorized,
AuditActionAuthCancelled: AuditResDBInstanceAuthCancelled,
AuditActionFrozen: AuditResDBInstanceFrozen,
AuditActionThawed: AuditResDBInstanceThawed,
}
package audit
import (
"context"
"time"
"github.com/anybackup-ai/Zeus/_git/framework_go-thrifts/ncaudittype"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
// AuditUserInfo 审计用户信息
type AuditUserInfo struct {
UserId string
UserName string
LoginIp string
TenantId string
}
// AuditService 审计服务接口
type AuditService interface {
// ReportInstanceAudit 上报实例审计日志
// propertySet: 操作参数列表,可为nil或空数组
ReportInstanceAudit(c *gin.Context, action AuditAction, instanceName, resourceId string, propertySet []*ncaudittype.NcAuditProperty) error
}
// auditService 审计服务实现
type auditService struct {
auditSvcRpc IAuditSvcRpc
}
// IAuditSvcRpc 审计服务RPC接口(便于测试时mock)
type IAuditSvcRpc interface {
SaveAuditObject(ctx context.Context, auditObject *ncaudittype.NcAuditObject) error
SaveAuditLog(ctx context.Context, auditLog *ncaudittype.NcAuditLogContent) error
}
// NewAuditService 创建审计服务实例
func NewAuditService(rpc IAuditSvcRpc) AuditService {
return &auditService{
auditSvcRpc: rpc,
}
}
// getAuditUserInfo 从请求上下文获取用户信息
func (s *auditService) getAuditUserInfo(c *gin.Context) (*AuditUserInfo, error) {
// 根据项目实际情况获取用户信息
// 示例:
,
}
*ncaudittype.NcAuditObjectProperty {
&ncaudittype.NcAuditObjectProperty{
Name: &ncaudittype.NcLanguageText{
Text: nameResCode,
},
Value: []{value},
}
}
[]*ncaudittype.NcAuditObjectProperty {
[]*ncaudittype.NcAuditObjectProperty{
buildObjectProperty(, instanceName),
}
}
{
time.Now().UnixNano() /
}
*ncaudittype.NcAuditProperty {
&ncaudittype.NcAuditProperty{
Name: &ncaudittype.NcLanguageText{
Text: nameResCode,
},
Value: []{value},
}
}
ReportInstanceAudit(c *gin.Context, action AuditAction, instanceName, resourceId , propertySet []*ncaudittype.NcAuditProperty) {
userInfo, err := s.getAuditUserInfo(c)
err != {
logrus.Errorf(, err)
err
}
objectProperties := buildInstanceProperties(instanceName)
auditObject := &ncaudittype.NcAuditObject{
ObjectId: resourceId,
UserId: userInfo.UserId,
TenantId: userInfo.TenantId,
ObjectName: instanceName,
ObjectType: ncaudittype.NcAuditObjectType_ProtectObject,
ObjectProperty: objectProperties,
}
ctx := c.Request.Context()
err = s.auditSvcRpc.SaveAuditObject(ctx, auditObject)
err != {
logrus.Errorf(, err)
}
resCode, ok := AuditActionToResCode[action]
!ok {
logrus.Warnf(, action)
}
auditLog := &ncaudittype.NcAuditLogContent{
UserId: userInfo.UserId,
TriggerTime: getCurrentTimeMillis(),
IP: userInfo.LoginIp,
Description: &ncaudittype.NcLanguageText{
Text: (resCode),
Params: []{
: userInfo.UserName,
: time.Now().Format(),
: instanceName,
},
},
AuditObjectId: resourceId,
PropertySet: propertySet,
}
err = s.auditSvcRpc.SaveAuditLog(ctx, auditLog)
err != {
logrus.Errorf(, err)
err
}
}
创建 ext-rpc/audit_svc.go:
package ext_rpc
import (
"context"
"time"
"github.com/anybackup-ai/Flora/_git/greed/rpc"
"github.com/anybackup-ai/Zeus/_git/framework_go-thrifts/ncauditsvc"
"github.com/anybackup-ai/Zeus/_git/framework_go-thrifts/ncaudittype"
hrc_core "github.com/anybackup-ai/Zeus/_git/service_resource-center_hrc-core"
hrc_util "github.com/anybackup-ai/Zeus/_git/service_resource-center_hrc-core/util"
"github.com/apache/thrift/lib/go/thrift"
"github.com/sirupsen/logrus"
)
// IAuditSvcService 审计服务RPC接口
type IAuditSvcService interface {
SaveAuditLog(ctx context.Context, auditLog *ncaudittype.NcAuditLogContent) error
SaveAuditObject(ctx context.Context, auditObject *ncaudittype.NcAuditObject) error
GetAuditObject(ctx context.Context, auditObjectId string) (*ncaudittype.NcAuditObject, error)
}
// AuditSvcService 审计服务实现
type AuditSvcService struct {
ThriftClient rpc.ThriftClient
TimeOut int
SvcName string
}
// NewAuditSvcService 创建审计服务客户端
func NewAuditSvcService(ip string, port int) IAuditSvcService {
auditService := &AuditSvcService{
TimeOut: 30,
SvcName: "AuditService",
}
client, err := hrc_util.NewThriftClient(ip, port)
if err != nil {
hrc_core.Logger.Errorf("new audit service thrift client failed")
return nil
}
auditService.ThriftClient = client
return auditService
}
clientHolder {
transport thrift.TTransport
client *ncauditsvc.NcAuditSvcClient
}
createClient() (*clientHolder, ) {
transport, protocol, err := a.ThriftClient.CreateExternalRpcParam()
err != {
logrus.Errorf(, err)
, err
}
err = transport.Open(); err != {
logrus.Errorf(, err)
, err
}
client := ncauditsvc.NewNcAuditSvcClientProtocol(transport, protocol, protocol)
&clientHolder{
transport: transport,
client: client,
},
}
SaveAuditLog(ctx context.Context, auditLog *ncaudittype.NcAuditLogContent) {
ctx, cancel := context.WithTimeout(ctx, time.Duration(a.TimeOut)*time.Second)
cancel()
holder, err := a.createClient()
err != {
err
}
{
_ = holder.transport.Close()
}()
err = holder.client.SaveAuditLog(ctx, auditLog)
err != {
logrus.Error(err)
err
}
}
SaveAuditObject(ctx context.Context, auditObject *ncaudittype.NcAuditObject) {
ctx, cancel := context.WithTimeout(ctx, time.Duration(a.TimeOut)*time.Second)
cancel()
holder, err := a.createClient()
err != {
err
}
{
_ = holder.transport.Close()
}()
err = holder.client.SaveAuditObject(ctx, auditObject)
err != {
logrus.Error(err)
err
}
}
GetAuditObject(ctx context.Context, auditObjectId ) (*ncaudittype.NcAuditObject, ) {
ctx, cancel := context.WithTimeout(ctx, time.Duration(a.TimeOut)*time.Second)
cancel()
holder, err := a.createClient()
err != {
, err
}
{
_ = holder.transport.Close()
}()
result, err := holder.client.GetAuditObject(ctx, auditObjectId)
err != {
logrus.Error(err)
, err
}
result,
}
在 global/init.go 中添加:
var auditSvcRpc extrpc.IAuditSvcService
func GetAuditSvcRpc() extrpc.IAuditSvcService {
if auditSvcRpc != nil {
return auditSvcRpc
}
envConfig, err := hrc_core.GetEnvConfig()
if err != nil {
GetLog().Errorf("GetEnvConfig failed")
return nil
}
auditSvcRpc = extrpc.NewAuditSvcService(
hrc_core.GetSelfIp(),
envConfig.ServerIni.DefaultInt("AuditService::thrift", 9125),
)
return auditSvcRpc
}
func CreateInstance(c *gin.Context) {
// ... 业务逻辑 ...
// 异步上报审计日志(推荐)
go func() {
auditSvc := audit.NewAuditService(global.GetAuditSvcRpc())
err := auditSvc.ReportInstanceAudit(c, audit.AuditActionCreated, instanceName, resourceId, nil)
if err != nil {
logrus.Errorf("report audit log failed: %v", err)
}
}()
// ... 返回响应 ...
}
func CreateInstance(c *gin.Context) {
// ... 业务逻辑 ...
// 异步上报审计日志(带操作参数)
go func() {
// 构建操作参数
// 注意:直接上报原始编码值,不做转换,由前端或审计服务端处理国际化
propertySet := []*ncaudittype.NcAuditProperty{
audit.BuildAuditProperty("Audit.DBDBType", fmt.Sprintf("%d", req.Type)), // 数据库类型原始编码
audit.BuildAuditProperty("Audit.DBDBName", req.DbName), // 数据库名
}
auditSvc := audit.NewAuditService(global.GetAuditSvcRpc())
err := auditSvc.ReportInstanceAudit(c, audit.AuditActionCreated, instanceName, resourceId, propertySet)
if err != nil {
logrus.Errorf("report audit log failed: %v", err)
}
}()
// ... 返回响应 ...
}
重要原则:
注意:以下JSON格式仅供参考,请根据实际业务需求定义资源码和描述内容。
在 i18nlanguageresources 仓库的以下文件中添加:
zh_CN/event/Audit_event.json:
{
"Audit.DBInstanceCreated": {
"resId": "Audit.DBInstanceCreated",
"description": "用户[userName]于[time]创建了数据库实例[instanceName]"
}
}
en_US/event/Audit_event.json:
{
"Audit.DBInstanceCreated": {
"resId": "Audit.DBInstanceCreated",
"description": "User[userName]created database instance[instanceName]at[time]"
}
}
zh_CN/resource/Audit_resource.json:
{
"Audit.ObjectName": {
"resId": "Audit.ObjectName",
"description": "对象名称"
},
"Audit.ObjectType": {
"resId": "Audit.ObjectType",
"description": "对象类型"
},
"Audit.DBDBType": {
"resId": "Audit.DBDBType",
"description": "数据库类型"
},
"Audit.DBDBName": {
"resId": "Audit.DBDBName",
"description": "数据库名"
},
"Audit.DBAuthType": {
"resId": "Audit.DBAuthType",
en_US/resource/Audit_resource.json:
{
"Audit.DBDBType": {
"resId": "Audit.DBDBType",
"description": "Database Type"
},
"Audit.DBDBName": {
"resId": "Audit.DBDBName",
"description": "Database Name"
},
"Audit.DBAuthType": {
"resId": "Audit.DBAuthType",
"description": "Authorization Type"
},
"Audit.DBAuthMode": {
"resId": "Audit.DBAuthMode",
"description": "Authentication Mode"
},
"Audit.DBClusterType": {
"resId": "Audit.DBClusterType"
ObjectProperty 字段,包含对象名称和类型Audit.DBxxx 格式(如 Audit.DBDBType、Audit.DBClusterType)Audit.DB2xxx 前缀(如 Audit.DB2InstanceCreated)NewAuditServiceWithRpc 注入mock RPC实现进行单元测试Q: 审计日志上报失败会影响主业务吗? A: 不应该。审计日志上报应该使用异步方式,且失败只记录日志,不返回错误给调用方。
Q: 如何支持新的操作类型?
A: 1) 在 audit_constants.go 中添加新的Action常量;2) 添加对应的资源码映射;3) 在i18n资源文件中添加描述。
Q: 对象属性中的资源码在哪里定义?
A: 对象属性名称(如 Audit.ObjectName、Audit.ObjectType)在 i18nlanguageresources 仓库的 resource/Audit_resource.json 文件中定义;审计日志描述(如 Audit.DBInstanceCreated)在 event/Audit_event.json 文件中定义。
Q: 审计日志描述没有成功国际化怎么办? A: 请检查以下几点:
i18nlanguageresources 仓库的 event/Audit_event.json 中已定义NcLanguageText 的 Params 字段正确填充了参数Params 字段Q: 如何为审计日志添加操作参数(PropertySet)? A: 参考以下步骤:
i18nlanguageresources 仓库的 resource/Audit_resource.json 中定义操作参数名称的资源码(如 Audit.DBDBType)buildAuditProperty 辅助函数构建 NcAuditProperty 对象ReportInstanceAudit 时传入 propertySet 参数Q: 操作参数中应该包含哪些信息? A: 建议包含:
当 DB2 备份配置创建或更新时,需要全量上报所有配置参数(约 70+ 个参数)到审计日志的 PropertySet 中,以便审计服务能够展示配置变更前后的参数对比。
向后兼容性设计:
ReportProtectObjectAuditWithGinAndPropertySet,保持 ReportProtectObjectAuditWithGin 不变ReportProtectObjectAudit 方法签名,添加 propertySet 参数ReportProtectObjectAuditWithGin分模块构建:
BuildDB2BackupConfigPropertySet 调用所有子构建函数参数结构体:
CommonBackupParam(24 个参数)DB2MultiSessionParam(2 个参数)DB2BufferParam(4 个参数)DB2PriorityParam(2 个参数)DB2BackupParam(35 个参数)SANGatewayInfo(7 个参数)DB2BackupScriptsParam(8 个参数)直接转换为字符串:
bool → fmt.Sprintf("%t", value)int32/int64 → fmt.Sprintf("%d", value)float64 → fmt.Sprintf("%f", value)string → 直接使用[]string → strings.Join(value, ",")敏感信息过滤:
AdgPrimaryPwd 字段(密码)资源码命名:
Audit.DB2xxx 前缀(如 Audit.DB2StoragePoolType、Audit.DB2BackupGran)资源文件位置:
i18nlanguageresources/zh_CN/resource/Audit_resource.jsoni18nlanguageresources/en_US/resource/Audit_resource.json修改 UpdateBackupConfig 方法:
// 构建 PropertySet(全量上报所有参数)
propertySet := audit.BuildDB2BackupConfigPropertySet(&req.DB2BackupConfig)
// 上报审计日志
err = utils.ReportProtectObjectAuditWithGinAndPropertySet(c, req.ObjectId, objectName,
"Audit.DB2ProtectConfigUpdated", map[string]string{
"userName": userInfo.UserName,
}, propertySet)
向后兼容性:通过添加新函数保持向后兼容,不影响现有代码 全量上报:所有参数(除密码外)都上报到 PropertySet 异步上报:使用 goroutine 异步上报,不影响主业务流程 敏感信息过滤:只过滤密码字段,不上报到审计日志
Q: 为什么操作参数要上报原始编码值而不是可读名称? A: 原因如下:
严禁包含: