如何将Rails中的特定事件记录到日志文件中?

时间:2012-06-06 03:22:25

标签: ruby-on-rails ruby

要求

我想保留特定事件的审核日志,例如:

  • 用户已成功登录
  • 用户无法登录(原因:密码错误,未经证实等)
  • SuperUser修改了其他用户详细信息(包含更改内容)

此日志应包含以下详细信息:

  • 执行操作的登录用户(基于控制器的current_user
  • 正在修改的记录(例如超级用户的其他用户记录)
  • 该操作请求的IP地址

此日志也应该存储在文件系统上的文件中,而不是数据库表中,以便稍后可以被计算机上的其他服务摄取。

一些可能性

以下是我到目前为止所考虑的方法的简短列表:

的ActiveRecord ::观察

观察者为观看这些特定事件提供了一种很好的分离方式 然后我可以让观察者附加到日志文件,但我不确定我能够轻松获得这样的调用结果(例如登录失败或工作),我需要以某种方式调用控制器方法current_user找出登录用户并获取HTTP请求以获取IP地址。

审计宝石(如审计,审计,paper_trail等)

这些宝石可以方便地了解如何访问当前用户和IP地址的控制器,但它们都会记录到数据库中的审计表中。 Auditable特别好,因为它可以审计对象上的任何方法调用,而不仅仅是AR回调,但我可能需要修补它来写入文件而不是数据库......或者什么?

的ActiveSupport ::通知

我仍然需要阅读此内容,但我相信它提供了低级别的方式来订阅rails中的低级别事件。对于这种情况,这可能太低了,但我需要进一步调查。

log4r的

这似乎会成为一个不错的日志文件,但我认为它没有任何方式来观察事件。这只是问题的一部分。

有什么建议吗?

这样做有最佳实践方法吗?您能否推荐从以往经验中学到的宝石或经验教训?还有什么我应该考虑的吗?

4 个答案:

答案 0 :(得分:1)

一些想法:

审核宝石听起来最接近你想要的。如果你看一下它们的源代码,它们并不复杂,而且大多数似乎都是围绕Rails Observers构建的。您可以轻松地使用他们的代码作为基础来创建自己的专用版本。

如果你看一下他的Audited代码,你会发现存储current_user实际上很简单(如果你决定推出自己的实现):

https://github.com/collectiveidea/audited/blob/master/lib/audited/sweeper.rb

我认为DB中的审核数据不一定是坏的。实际上,当您需要跟踪数千个先前交易中的一些复杂问题时,您可能会发现它很有用。您始终可以创建一个简单的Rake任务,以便在需要时将数据转储为日志文件格式。

但是我会说Log4r非常好,我自己在几个项目中使用它。但我没有审计类型的需求。仅用于调试和故障排除的基本日志。

如果你想要的话,你也可以考虑将自己的Observer类型系统与Log4r驱动程序而不是数据库驱动程序结合起来。因为它听起来有点像你可能需要在Observer系统提供的东西之外触发记录事件。这意味着您将不得不实现对现有gem的扩展,或者使用gem作为基础并使用您自己的功能扩展它。

无论如何 - 我仍然认为数据库方法实际上是一种好处,并且能够在审计跟踪上运行查询有点酷。这样的东西不会受到伤害(Log4r顺便说一句也支持自定义“输出驱动程序”),所以即使这样也可以用来登录数据库)。

答案 1 :(得分:1)

感谢大家的回复 卡斯帕,我确实决定建立自定义的东西。

无论如何,我看到了写入本地数据库的观点,但是这个项目的要求是转储日志文件,以便更精细的日志解析服务可以查询文件,甚至将它们与来自其他来源的信息结合起来。

为了从模型和控制器中获取日志记录,我最终创建了一个模块,我将在观察者和ApplicationController中包含这些模块。

该模块看起来像这样:

module MyEventLogger
  mattr_accessor :logged_current_user
  mattr_accessor :logged_remote_ip

  def log_event(message)
    @@logger ||= Logger.new(Rails.root.join('log', 'audit.log'))
    @@logger.info "#{Time.now} | #{logged_current_user}@#{logged_remote_ip} | #{message}"
  end

  def logged_current_user
    @@logged_current_user || "SYSTEM"
  end

  def logged_remote_ip
    @@logged_remote_ip || "NO IP ADDRESS"
  end
end

ApplicationController将具有:

include MyEventLogger
before_filter :setup_logger

...

def setup_logger
  MyEventLogger.logged_current_user = current_user
  MyEventLogger.logged_ip_address = request.remote_ip
end

观察者只需要include MyEventLogger,它就可以访问log_event方法和当前用户和IP地址。例如:

class UserObserver < ActiveRecord::Observer
  include MyEventLogger

  def after_save(user)
    log_event "The User #{user} was saved by #{logged_current_user}"
  end

end

答案 2 :(得分:0)

与我分享我的个人经历:

我创造的东西与你在这里提到的非常相似。我在数据库中使用了一个表,在application_controller中捕获了相关的post请求,并将信息传递给与我的日志表关联的模型。逻辑非常简单,我拥有了我想要的所有控制权。唯一的努力是选择/拒绝特定的交易,并重组所有有用的参数,以便很好地适应文本字段。

如果您决定采取这种方式,我将很乐意分享更多细节。

祝你好运。

答案 3 :(得分:0)

这样做的一种方法是,您希望运行已记录的操作,在块

中运行它

我真的建议看看Russ Olsen的{Eooquent Ruby第18章http://books.google.com/books?id=-s2xL0pVsLUC&lpg=PA219&ots=l7I3oAK3M2&dq=eloquent%20ruby%20chapter%2018&pg=PA219#v=onepage&q&f=false,以及Gregory Brown的Ruby Best Practices第2章中的'Using Blocks'http://majesticseacreature.com/rbp-book/pdfs/rbp_1-0.pdf

e.g。

def with_logging(description)
 begin
    @logger.debug( "Starting #{description}" ) 
    yield # this is when the code in the block executes
    @logger.debug( "Completed #{description}" )
  rescue
    @logger.error( "#{description} failed!!") 
    raise
  end
end

with_logging('code example') { puts "just printing something" }

此外:可能值得研究graylog http://graylog2.org/about/gelf等工具(并参阅http://arrrrcamp.be/videos/2011/lennart-koopmann---managing-the-logs-of-your-rails-applications/)或这些帖子http://openmymind.net/2012/4/4/You-Really-Should-Log-Client-Side-Error/(javascript),https://github.com/TwP/logging,{{3} }