Created
July 30, 2024 02:04
-
-
Save valexandersaulys/fcb2a8f80d221d86356bbe44e8d4228a to your computer and use it in GitHub Desktop.
Audit Log in Gorm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package models | |
| // https://aayushacharya.com.np/blog/audit-log-gorm/ | |
| // couldn't get it to work, error inlined towards bottom | |
| import ( | |
| "encoding/json" | |
| "fmt" | |
| log "github.com/sirupsen/logrus" | |
| "gorm.io/datatypes" | |
| "gorm.io/gorm" | |
| "reflect" | |
| "time" | |
| ) | |
| type AuditLog struct { | |
| ID string `json:"id" gorm:"primaryKey;autoIncrement"` | |
| TableName string `json:"table_name"` | |
| OperationType string `json:"operation_type"` | |
| ObjectId string `json:"object_id"` | |
| Data datatypes.JSON `json:"data"` | |
| CreatedAt time.Time `json:"created_at"` | |
| } | |
| func getKeyFromData(key string, data map[string]interface{}) string { | |
| objId, ok := data[key] | |
| if !ok { | |
| return "" | |
| } | |
| return objId.(string) | |
| } | |
| func prepareData(data map[string]interface{}) datatypes.JSON { | |
| dataByte, _ := json.Marshal(&data) | |
| return dataByte | |
| } | |
| func createAuditLog(db *gorm.DB) { | |
| if db.Statement.Schema != nil && db.Statement.Schema.Table == "audit_logs" || db.Error != nil { | |
| return | |
| } | |
| recordMap, err := getDataBeforeOperation(db) | |
| if err != nil { | |
| return | |
| } | |
| objId := getKeyFromData("id", recordMap) | |
| auditLog := &AuditLog{ | |
| TableName: db.Statement.Schema.Table, | |
| OperationType: "CREATE", | |
| ObjectId: objId, | |
| Data: prepareData(recordMap), | |
| } | |
| if err := db.Session(&gorm.Session{SkipHooks: true, NewDB: true}).Table("audit_logs").Create(auditLog).Error; err != nil { | |
| log.Error(fmt.Errorf("error in audit log creation: %s", err.Error())) | |
| return | |
| } | |
| } | |
| func updateAuditLog(db *gorm.DB) { | |
| fmt.Println("Update Data") | |
| if db.Statement.Schema != nil && db.Statement.Schema.Table == "audit_logs" || db.Error != nil { | |
| return | |
| } | |
| recordMap, err := getDataBeforeOperation(db) | |
| if err != nil { | |
| return | |
| } | |
| objId := getKeyFromData("id", recordMap) | |
| auditLog := &AuditLog{ | |
| TableName: db.Statement.Schema.Table, | |
| OperationType: "UPDATE", | |
| ObjectId: objId, | |
| Data: prepareData(recordMap), | |
| } | |
| if err := db.Session(&gorm.Session{SkipHooks: true, NewDB: true}).Table("audit_logs").Create(auditLog).Error; err != nil { | |
| log.Error(fmt.Errorf("error in audit log creation: %s", err.Error())) | |
| return | |
| } | |
| } | |
| func deleteAuditLog(db *gorm.DB) { | |
| fmt.Println("DELETE Data") | |
| if db.Statement.Schema != nil && db.Statement.Schema.Table == "audit_logs" || db.Error != nil { | |
| return | |
| } | |
| recordMap, err := getDataBeforeOperation(db) | |
| if err != nil { | |
| return | |
| } | |
| objId := getKeyFromData("id", recordMap) | |
| auditLog := &AuditLog{ | |
| TableName: db.Statement.Schema.Table, | |
| OperationType: "DELETE", | |
| ObjectId: objId, | |
| Data: prepareData(recordMap), | |
| } | |
| if err := db.Session(&gorm.Session{SkipHooks: true, NewDB: true}).Table("audit_logs").Create(auditLog).Error; err != nil { | |
| log.Error(fmt.Errorf("error in audit log creation: %s", err.Error())) | |
| return | |
| } | |
| } | |
| func getDataBeforeOperation(db *gorm.DB) (map[string]interface{}, error) { | |
| fmt.Println("Get Data", db.Error) | |
| objMap := map[string]interface{}{} | |
| if db.Error == nil && !db.DryRun { | |
| objectType := reflect.TypeOf(db.Statement.ReflectValue.Interface()) | |
| // Create a new instance of the object type | |
| targetObj := reflect.New(objectType).Interface() | |
| primaryKeyValue := "" | |
| value := db.Statement.ReflectValue | |
| // Check if the value is a struct | |
| if value.Kind() == reflect.Struct { | |
| primaryKeyValue = value.FieldByName("Id").String() | |
| } | |
| // Fetch the target object separately | |
| if err := db.Session(&gorm.Session{SkipHooks: true, NewDB: true}).Where("id = ?", primaryKeyValue).First(&targetObj).Error; err != nil { | |
| // time="2024-07-29T22:03:46-04:00" level=error msg="gorm callback: error while finding target object: record not found " | |
| log.Error(fmt.Errorf("gorm callback: error while finding target object: %s %s", err.Error(), primaryKeyValue)) | |
| return nil, err | |
| } | |
| jsonBytes, err := json.Marshal(targetObj) | |
| if err != nil { | |
| log.Error(fmt.Errorf("gorm callback: error while marshalling json data: %s", err.Error())) | |
| return nil, err | |
| } | |
| json.Unmarshal(jsonBytes, &objMap) | |
| } | |
| return objMap, nil | |
| } | |
| func RegisterAuditLogCallbacks(db *gorm.DB) error { | |
| db.Callback().Create().After("gorm:create").Register("custom_plugin:create_audit_log", createAuditLog) | |
| db.Callback().Update().After("gorm:update").Register("custom_plugin:update_audit_log", updateAuditLog) | |
| db.Callback().Delete().Before("gorm:delete").Register("custom_plugin:delete_audit_log", deleteAuditLog) | |
| return nil | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment