Skip to content

Instantly share code, notes, and snippets.

@valexandersaulys
Created July 30, 2024 02:04
Show Gist options
  • Select an option

  • Save valexandersaulys/fcb2a8f80d221d86356bbe44e8d4228a to your computer and use it in GitHub Desktop.

Select an option

Save valexandersaulys/fcb2a8f80d221d86356bbe44e8d4228a to your computer and use it in GitHub Desktop.
Audit Log in Gorm
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