業務システムで問い合わせやトラブルが起きたとき、
「誰がいつ何をしたか」を答えられるかが対応の質を決めます。
何を記録するか
すべてを記録すると量が膨大になり、肝心なものが埋もれます。
優先度は次の順です。
必須
- 認証(ログイン成功・失敗・ログアウト)
- 権限の変更
- データの削除
- 金額に関わる変更
- 個人情報の閲覧・出力
あると良い
- レコードの作成・更新
- 設定変更
- 一括処理の実行
通常は不要
- 一覧画面の表示
- 検索
実装
class AuditLog(models.Model):
actor = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
actor_label = models.CharField(max_length=120) # 退会後も残す表示名
action = models.CharField(max_length=40) # create / update / delete / export
target_model = models.CharField(max_length=60)
target_id = models.CharField(max_length=40)
changes = models.JSONField(null=True) # 変更前後
ip = models.GenericIPAddressField(null=True)
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
actor_label を別に持つのが要点です。
ユーザーが削除されても「誰が」が残ります。
外部キーだけだと NULL になり、追跡できなくなります。
変更前後を記録する
def diff(before: dict, after: dict) -> dict:
return {
k: {"before": before.get(k), "after": after.get(k)}
for k in set(before) | set(after)
if before.get(k) != after.get(k)
}
変わった項目だけを記録します。全項目を残すと量が跳ね上がります。
パスワードや個人情報は、値そのものではなく変更事実だけを残します。
MASKED = {"password", "card_number", "my_number"}
changes = {k: ("(変更)" if k in MASKED else v) for k, v in changes.items()}
記録漏れを防ぐ
各ビューで書くと必ず漏れます。シグナルで自動化します。
@receiver(post_delete)
def log_delete(sender, instance, **kwargs):
if sender in AUDITED_MODELS:
AuditLog.objects.create(
actor=current_user(), action="delete",
target_model=sender.__name__, target_id=str(instance.pk),
)
シグナルからリクエスト情報を取るには、
ミドルウェアで現在のユーザーを保持する仕組みが必要です。
論理削除と組み合わせる
監査ログに「削除した」と残っていても、
中身が消えていれば何を削除したのか分かりません。
class SoftDeleteModel(models.Model):
deleted_at = models.DateTimeField(null=True, db_index=True)
class Meta:
abstract = True
論理削除にしてゴミ箱から復元できるようにすると、
誤操作の救済もできます。業務システムでは物理削除を避けるのが基本です。
見られる形にする
記録しても、検索できなければ使われません。
管理画面から絞り込めるようにします。
・ユーザーで絞る
・期間で絞る
・対象レコードで絞る(この注文に何が起きたか)
「この1件の履歴」が時系列で見られると、問い合わせ対応が非常に速くなります。
保存期間と改ざん防止
- 保存期間を決める(1年、3年など)
- アプリから更新・削除できないようにする(追記のみ)
- 古いものは別ストレージへ退避
監査ログを管理画面から編集できる状態だと、証跡としての価値がありません。
まとめ
- 認証・権限変更・削除・金額・個人情報の閲覧は必須で記録
actor_labelを別に持ち、ユーザー削除後も追える形に- 変更は差分のみ。秘密情報は値を残さない
- シグナルで自動化して記録漏れを防ぐ
- 論理削除と組み合わせる
- 検索できる形にし、追記のみに制限する