如何在 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
一般来说,这很好用,但在使用“magic”时会失败" 附加到 has_many :through => 的
方法协会。
例如:
# model
class MyModel
has_many :runway_models, :dependent => :destroy
has_many :runways, :through => :runway_models
end
#controller
class MyModelController < ApplicationController
# ...
# params => {: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
我的问题是... 有没有办法让它工作,以便它观察这些更改(和/或可能的其他删除)?
是否有更好且相对不引人注目的解决方案?
We recently began a compliance push at our company and are required to keep a full history of changes to our data which is currently managed in a Rails application. We've been given the OK to simply push something descriptive for every action to a log file, which is a fairly unobtrusive way to go.
My inclination is to do something like this in ApplicationController
:
around_filter :set_logger_username
def set_logger_username
Thread.current["username"] = current_user.login || "guest"
yield
Thread.current["username"] = nil
end
Then create an observer that looks something like this:
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
and in general this works great, but it fails when using the "magic" <association>_ids
method that is tacked to has_many :through => associations.
For instance:
# model
class MyModel
has_many :runway_models, :dependent => :destroy
has_many :runways, :through => :runway_models
end
#controller
class MyModelController < ApplicationController
# ...
# params => {: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
This will end up triggering the after_create
when new Runway
records are associated, but will not trigger the before_destroy
when a RunwayModel
is deleted.
My question is...
Is there a way to make it work so that it will observe those changes (and/or potentially other deletes)?
Is there a better solution that is still relatively unobtrusive?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我最近的一个项目也有类似的要求。我结束了使用 acts_as_audited gem,它对我们来说非常有用。
在我的应用程序控制器中,我有如下所示的行
,它负责所有的魔法,它还保留所做的所有更改以及谁进行的更改的日志 - 它非常光滑。
希望有帮助
I had a similar requirement on a recent project. I ended using the acts_as_audited gem, and it worked great for us.
In my application controller I have line like the following
and it takes care of all the magic, it also keeps a log of all the changes that were made and who made them-- its pretty slick.
Hope it helps
为此,请使用 Vestal 版本插件:
请参阅 此屏幕截图了解更多详细信息。查看 类似最近在这里回答了问题。
Vestal versions
插件是最活跃的插件,它只存储增量。属于不同模型的增量存储在一张表中。Use the Vestal versions plugin for this:
Refer to this screen cast for more details. Look at the similar question answered here recently.
Vestal versions
plugin is the most active plugin and it only stores delta. The delta belonging to different models are stored in one table.将此猴子补丁添加到我们的
lib/core_extensions.rb
它会影响性能(!),但满足要求,并且考虑到这个 destroy_all 不会经常被调用,它适用于我们的需要——尽管我要查看acts_as_versioned 和acts_as_audited
Added this monkey-patch to our
lib/core_extensions.rb
It is a performance hit(!), but satisfies the requirement and considering the fact that this destroy_all doesn't get called often, it works for our needs--though I am going to check out acts_as_versioned and acts_as_audited
您还可以使用类似acts_as_versioned http://github.com/technoweenie/acts_as_versioned
它对您的表记录进行版本控制,并在每次发生更改时创建一个副本(例如在 wiki 中)
这比日志文件更容易审核(显示界面中的差异等)
You could also use something like acts_as_versioned http://github.com/technoweenie/acts_as_versioned
It versions your table records and creates a copy every time something changes (like in a wiki for instance)
This would be easier to audit (show diffs in an interface etc) than a log file