如何为每个表在Rails中创建完整的审核日志?

我们最近开始在我们公司进行合规性推动,并且需要保留我们目前在Rails应用程序中管理的数据的完整历史记录。 我们已经可以将每个操作的描述性内容简单地推送到日志文件中,这是一种相当不引人注目的方式。

我倾向于在ApplicationController做这样的事情:

 around_filter :set_logger_username def set_logger_username Thread.current["username"] = current_user.login || "guest" yield Thread.current["username"] = nil end 

然后创建一个看起来像这样的观察者:

 class AuditObserver < ActiveRecord::Observer observe ... #all models that need to be observed def after_create(auditable) AUDIT_LOG.info "[#{username}][ADD][#{auditable.class.name}][#{auditable.id}]:#{auditable.inspect}" end def before_update(auditable) AUDIT_LOG.info "[#{username}][MOD][#{auditable.class.name}][#{auditable.id}]:#{auditable.changed.inspect}" end def before_destroy(auditable) AUDIT_LOG.info "[#{username}][DEL][#{auditable.class.name}][#{auditable.id}]:#{auditable.inspect}" end def username (Thread.current['username'] || "UNKNOWN").ljust(30) end end 

并且通常这很好用 ,但是当使用添加到has_many:through =>关联的“magic” _ids方法时它会失败。

例如:

 # model class MyModel has_many :runway_models, :dependent => :destroy has_many :runways, :through => :runway_models end #controller class MyModelController  {:my_model => {:runways_ids => ['1', '2', '3', '5', '8']}} def update respond_to do |format| if @my_model.update_attributes(params[:my_model]) flash[:notice] = 'My Model was successfully updated.' format.html { redirect_to(@my_model) } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @my_model.errors, :status => :unprocessable_entity } end end end # ... end 

这将在新的Runway记录关联时最终触发after_create ,但在删除RunwayModel时不会触发before_destroy

我的问题是……是否有办法让它发挥作用,以便观察这些变化(和/或可能的其他删除)?
有没有更好的解决方案仍然相对不引人注目?

我对最近的一个项目有类似的要求。 我结束了使用acts_as_audited gem,它对我们很有用。

在我的应用程序控制器中,我有如下行

 audit RunWay,RunWayModel,OtherModelName 

它可以处理所有的魔法,它还会记录所有变化以及制作它们的变化 – 它非常漂亮。

希望能帮助到你

使用Vestal版本插件 :

有关更多详细信息,请参阅此屏幕演示。 看看最近在这里回答的类似问题 。

Vestal versions插件是最活跃的插件,它只存储delta。 属于不同模型的增量存储在一个表中。

 class User < ActiveRecord::Base versioned end # following lines of code is from the readme >> u = User.create(:first_name => "Steve", :last_name => "Richert") => # >> u.version => 1 >> u.update_attribute(:first_name, "Stephen") => true >> u.name => "Stephen Richert" >> u.version => 2 >> u.revert_to(10.seconds.ago) => 1 >> u.name => "Steve Richert" >> u.version => 1 >> u.save => true >> u.version => 3 

将这个猴子补丁添加到我们的lib/core_extensions.rb

 ActiveRecord::Associations::HasManyThroughAssociation.class_eval do def delete_records(records) klass = @reflection.through_reflection.klass records.each do |associate| klass.destroy_all(construct_join_attributes(associate)) end end end 

这是一个性能损失(!),但满足要求并考虑到这个destroy_all不经常被调用的事实,它适用于我们的需求 – 虽然我将检查acts_as_versioned和acts_as_audited

您还可以使用acts_as_versioned http://github.com/technoweenie/acts_as_versioned之类的内容
它会对您的表记录进行版本化,并在每次更改时创建副本(例如在wiki中)
这比审核文件更容易审核(在界面中显示差异等)