2011-05-13 14 views
17

Tengo un gran proyecto con los modelos ActiveRecord y ActiveResource. Necesito implementar el registro de la actividad del usuario con estos modelos y también registrar los cambios de los atributos del modelo (guardar el estado del objeto o algo así). Los cambios pueden ser realizados por usuarios o por tareas cron rake.Sistema de auditoría de rieles con ActiveResource y ActiveRecord

también he de tener la posibilidad de buscar los datos por fecha, cualquier campo .etc

será bueno también para generar mensajes legibles con la última actividad, por ejemplo

  • usuario cambio de Bob su contraseña para * y correo electrónico para ** en 2011-08-12 08:12
  • personal Jeff añadido nuevo par tner: Nombre de la empresa en 2011-08-12 08:13
  • Admin Jack producto eliminado: Nombre del producto en 2011-09-12 11:11
  • Cliente Sam ordenó nuevo servicio: Nombre del servicio en 2011-09-12 11 : 12

¿Alguien implementa tal registro? Ideas? Consejos?

¿debo usar gemas o puedo hacer toda la lógica con los observadores que no cambian los modelos?


me gusta joya https://github.com/airblade/paper_trail puede alguien decir ¿cómo puedo hacer que funcione con ActiveResource?

+0

Qué quiere decir el cambio de datos en sí o el cambio o esquema? Estas son dos cosas diferentes. Mi respuesta está dirigida a los datos ... – lzap

+0

cambiando los datos, no al esquema – Fivell

Respuesta

1

La gema acts_as_audited debería funcionar bien para usted:
https://github.com/collectiveidea/acts_as_audited

Y en lo que se considera ActiveResource, sino que también será un modelo de alguna otra aplicación. Puede usar la gema en el lado del servidor, y no necesita auditarla en el lado del cliente. Todas las operaciones CRUD que usan ActiveResource finalmente se traducirían a operaciones CRUD en ActiveRecord (en el lado del servidor).

Por lo tanto, probablemente tenga que mirarlo desde la distancia, y la misma solución se aplicaría en ambos casos, pero en diferentes lugares.

+0

si la auditoría estará tanto en el lado del servidor (Activeresource) como en el lado del cliente (ActiveRecord) ¿cómo puedo buscar, mostrar la última actividad ... etc.? Necesito un espacio de almacenamiento para todas las actividades Creo – Fivell

+0

De nuevo, depende de en qué lado desea ver la información. No he usado la gema pero estoy seguro de que estaría almacenando la información de auditoría en una tabla. si lo necesita del lado del servidor, siempre puede crear un modelo y acceder a él usando ActiveRecord, y si lo necesita del lado del cliente, puede crear otro objeto ActiveResource para acceder a la información de auditoría. Solo necesita ver dónde están los datos. Si está en el mismo lugar, debe usar ActiveRecord y si no, utilice ActiveResource para acceder a la información de manera remota. –

+0

Necesito almacenar información de auditoría en la base de datos del cliente sin depender de ActiveResource o ActiveRecord, no hay posibilidad de cambiar el código fuente de otra aplicación que comunico a través de ActiveResource – Fivell

4

Usted está buscando

https://github.com/collectiveidea/acts_as_audited

Pocos proyectos de código abierto utilizan ese plugin Creo que el mío Rojo, así como el capataz .

Editar: Desafortunadamente solo puede hacer ActiveRecord, no ActiveResource.

+0

gracias, pero ¿funciona con Activeresource? – Fivell

+0

No lo creo :-( – lzap

+0

yeap me to = (algunas otras ideas? – Fivell

3

https://github.com/collectiveidea/acts_as_audited

y

https://github.com/airblade/paper_trail

son ambos grandes soluciones para ActiveRecord solamente, pero ya que gran parte de ActiveRecord ha sido extraído a ActiveModel, es probable que sea razonable extender ya sea para apoyar ActiveResource como bueno, al menos para soporte de solo lectura. Miré a través de los gráficos de la red de Github y busqué en Google y no parece haber ningún desarrollo en curso de dicha solución, sin embargo, espero que sea más fácil de implementar sobre uno de estos dos complementos que comenzar desde cero. paper_trail parece estar en desarrollo más activo y tiene algunos compromisos para Rails 3.1, por lo que puede estar más actualizado con los componentes internos de Rails y más fácil de extender, pero eso es solo un instinto: no estoy familiarizado con el funcionamiento interno de ninguno de los dos.

+0

gracias por el consejo – Fivell

4

Fivell, acabo de ver esta pregunta y no tienen tiempo para trabajar hasta alteraciones de esta noche antes de que expire la recompensa, así que voy a dar mi código de auditoría que funciona con ActiveRecord y debería funcionar con ActiveResource, quizás con algunos ajustes (no uso ARes con la frecuencia suficiente como para saberlo). Sé que las devoluciones de llamada que usamos están ahí, pero no estoy seguro de si ARes tiene el atributo sucio changes de seguimiento de ActiveRecord.

Este código registra cada Crear/actualizar/eliminar en todos los modelos (excepto crea en el modelo de registro de auditoría y cualquier otro excepciones que especifique) con los cambios almacenados como JSON. También se almacena una traza inversa limpia para que pueda determinar qué código hizo el cambio (esto captura cualquier punto en su MVC, así como las tareas de rake y el uso de la consola).

Este código funciona para el uso de la consola, las tareas de rake y las solicitudes http, aunque generalmente solo el último registra al usuario actual. (Si mal no recuerdo, el observador de ActiveRecord que este reemplazó no funcionó en las tareas de rake o en la consola.) Oh, este código proviene de una aplicación de Rails 2.3. Tengo un par de aplicaciones de Rails 3, pero no he necesitado este tipo. de auditar para ellos todavía.

No tengo un código que cree una buena visualización de esta información (solo profundizamos en los datos cuando necesitamos investigar un problema), pero como los cambios se almacenan como JSON, debería ser bastante sencillo.

En primer lugar, almacenamos el usuario actual en User.current para que sea accesible en todas partes, por lo que en app/models/user.rb:

Class User < ActiveRecord::Base 
    cattr_accessor :current 
    ... 
end 

El usuario actual se establece en el controlador de aplicación para cada solicitud como tal (y no lo hace causar problemas de concurrencia):

def current_user 
    User.current = session[:user_id] ? User.find_by_id(session[:user_id]) : nil 
end 

También podemos establecer User.current en sus tareas rake si tenía sentido.

A continuación, definir el modelo para almacenar la información de auditoría app/models/audit_log_entry.rb - usted desea personalizar IgnoreClassesRegEx para adaptarse a cualquier modelo que no desea auditado:

# == Schema Information 
# 
# Table name: audit_log_entries 
# 
# id   :integer   not null, primary key 
# class_name :string(255) 
# entity_id :integer 
# user_id :integer 
# action  :string(255) 
# data  :text 
# call_chain :text 
# created_at :datetime 
# updated_at :datetime 
# 

class AuditLogEntry < ActiveRecord::Base 
    IgnoreClassesRegEx = /^ActiveRecord::Acts::Versioned|ActiveRecord.*::Session|Session|Sequence|SchemaMigration|CronRun|CronRunMessage|FontMetric$/ 
    belongs_to :user 

    def entity (reload = false) 
    @entity = nil if reload 
    begin 
     @entity ||= Kernel.const_get(class_name).find_by_id(entity_id) 
    rescue 
     nil 
    end 
    end 

    def call_chain 
    return if call_chain_before_type_cast.blank? 
    if call_chain_before_type_cast.instance_of?(Array) 
     call_chain_before_type_cast 
    else 
     JSON.parse(call_chain_before_type_cast) 
    end 
    end 
    def data 
    return if data_before_type_cast.blank? 
    if data_before_type_cast.instance_of?(Hash) 
     data_before_type_cast 
    else 
     JSON.parse(data_before_type_cast) 
    end 
    end 

    def self.debug_entity(class_name, entity_id) 
    require 'fastercsv' 
    FasterCSV.generate do |csv| 
     csv << %w[class_name entity_id date action first_name last_name data] 
     find_all_by_class_name_and_entity_id(class_name, entity_id, 
              :order => 'created_at').each do |a| 
     csv << [a.class_name, a.entity_id, a.created_at, a.action, 
      (a.user && a.user.first_name), (a.user && a.user.last_name), a.data] 
     end 
    end 
    end 
end 

A continuación añadimos algunos métodos para ActiveRecord::Base a hacer que las auditorías funcionen Deberá consultar el método audit_log_clean_backtrace y modificar según sus necesidades.(. Fwiw, ponemos adiciones a las clases existentes en lib/extensions/*.rb que se cargan en un inicializador) En lib/extensions/active_record.rb:

class ActiveRecord::Base 
    cattr_accessor :audit_log_backtrace_cleaner 
    after_create :audit_log_on_create 
    before_update :save_audit_log_update_diff 
    after_update :audit_log_on_update 
    after_destroy :audit_log_on_destroy 
    def audit_log_on_create 
    return if self.class.name =~ /AuditLogEntry/ 
    return if self.class.name =~ AuditLogEntry::IgnoreClassesRegEx 
    audit_log_create 'CREATE', self, caller 
    end 
    def save_audit_log_update_diff 
    @audit_log_update_diff = changes.reject{ |k,v| 'updated_at' == k } 
    end 
    def audit_log_on_update 
    return if self.class.name =~ AuditLogEntry::IgnoreClassesRegEx 
    return if @audit_log_update_diff.empty? 
    audit_log_create 'UPDATE', @audit_log_update_diff, caller 
    end 
    def audit_log_on_destroy 
    return if self.class.name =~ AuditLogEntry::IgnoreClassesRegEx 
    audit_log_create 'DESTROY', self, caller 
    end 
    def audit_log_create (action, data, call_chain) 
    AuditLogEntry.create :user  => User.current, 
         :action  => action, 
         :class_name => self.class.name, 
         :entity_id => id, 
         :data  => data.to_json, 
         :call_chain => audit_log_clean_backtrace(call_chain).to_json 
    end 
    def audit_log_clean_backtrace (backtrace) 
    if !ActiveRecord::Base.audit_log_backtrace_cleaner 
     ActiveRecord::Base.audit_log_backtrace_cleaner = ActiveSupport::BacktraceCleaner.new 
     ActiveRecord::Base.audit_log_backtrace_cleaner.add_silencer { |line| line =~ /\/lib\/rake\.rb/ } 
     ActiveRecord::Base.audit_log_backtrace_cleaner.add_silencer { |line| line =~ /\/bin\/rake/ } 
     ActiveRecord::Base.audit_log_backtrace_cleaner.add_silencer { |line| line =~ /\/lib\/(action_controller|active_(support|record)|hoptoad_notifier|phusion_passenger|rack|ruby|sass)\// } 
     ActiveRecord::Base.audit_log_backtrace_cleaner.add_filter { |line| line.gsub(RAILS_ROOT, '') } 
    end 
    ActiveRecord::Base.audit_log_backtrace_cleaner.clean backtrace 
    end 
end 

Por último, aquí están las pruebas que tenemos en esto - que necesita para modificar las acciones reales de la prueba por supuesto. test/integration/audit_log_test.rb

require File.dirname(__FILE__) + '/../test_helper' 

class AuditLogTest < ActionController::IntegrationTest 
    def setup 
    end 

    def test_audit_log 
    u = users(:manager) 
    log_in u 
    a = Alert.first :order => 'id DESC' 
    visit 'alerts/new' 
    fill_in 'alert_note' 
    click_button 'Send Alert' 
    a = Alert.first :order => 'id DESC', :conditions => ['id > ?', a ? a.id : 0] 
    ale = AuditLogEntry.first :conditions => {:class_name => 'Alert', :entity_id => a.id } 
    assert_equal 'Alert', ale.class_name 
    assert_equal 'CREATE', ale.action 
    end 

private 

    def log_in (user, password = 'test', initial_url = home_path) 
    visit initial_url 
    assert_contain 'I forgot my password' 
    fill_in 'email', :with => user.email 
    fill_in 'password', :with => password 
    click_button 'Log In' 
    end 

    def log_out 
    visit logout_path 
    assert_contain 'I forgot my password' 
    end 
end 

Y test/unit/audit_log_entry_test.rb:

# == Schema Information 
# 
# Table name: audit_log_entries 
# 
# id   :integer   not null, primary key 
# class_name :string(255) 
# action  :string(255) 
# data  :text 
# user_id :integer 
# created_at :datetime 
# updated_at :datetime 
# entity_id :integer 
# call_chain :text 
# 

require File.dirname(__FILE__) + '/../test_helper' 

class AuditLogEntryTest < ActiveSupport::TestCase 
    test 'should handle create update and delete' do 
    record = Alert.new :note => 'Test Alert' 
    assert_difference 'Alert.count' do 
     assert_difference 'AuditLogEntry.count' do 
     record.save 
     ale = AuditLogEntry.first :order => 'created_at DESC' 
     assert ale 
     assert_equal 'CREATE', ale.action, 'AuditLogEntry.action should be CREATE' 
     assert_equal record.class.name, ale.class_name, 'AuditLogEntry.class_name should match record.class.name' 
     assert_equal record.id, ale.entity_id, 'AuditLogEntry.entity_id should match record.id' 
     end 
    end 
    assert_difference 'AuditLogEntry.count' do 
     record.update_attribute 'note', 'Test Update' 
     ale = AuditLogEntry.first :order => 'created_at DESC' 
     expected_data = {'note' => ['Test Alert', 'Test Update']} 
     assert ale 
     assert_equal 'UPDATE', ale.action, 'AuditLogEntry.action should be UPDATE' 
     assert_equal expected_data, ale.data 
     assert_equal record.class.name, ale.class_name, 'AuditLogEntry.class_name should match record.class.name' 
     assert_equal record.id, ale.entity_id, 'AuditLogEntry.entity_id should match record.id' 
    end 
    assert_difference 'AuditLogEntry.count' do 
     record.destroy 
     ale = AuditLogEntry.first :order => 'created_at DESC' 
     assert ale 
     assert_equal 'DESTROY', ale.action, 'AuditLogEntry.action should be CREATE' 
     assert_equal record.class.name, ale.class_name, 'AuditLogEntry.class_name should match record.class.name' 
     assert_equal record.id, ale.entity_id, 'AuditLogEntry.entity_id should match record.id' 
     assert_nil Alert.find_by_id(record.id), 'Alert should be deleted' 
    end 
    end 

    test 'should not log AuditLogEntry create entry and block on update and delete' do 
    record = Alert.new :note => 'Test Alert' 
    assert_difference 'Alert.count' do 
     assert_difference 'AuditLogEntry.count' do 
     record.save 
     end 
    end 
    ale = AuditLogEntry.first :order => 'created_at DESC' 
    assert_equal 'CREATE', ale.action, 'AuditLogEntry.action should be CREATE' 
    assert_equal record.class.name, ale.class_name, 'AuditLogEntry.class_name should match record.class.name' 
    assert_equal record.id, ale.entity_id, 'AuditLogEntry.entity_id should match record.id' 
    assert_nil AuditLogEntry.first(:conditions => { :class_name => 'AuditLogEntry', :entity_id => ale.id }) 

    if ale.user_id.nil? 
     u = User.first 
    else 
     u = User.first :conditions => ['id != ?', ale.user_id] 
    end 
    ale.user_id = u.id 
    assert !ale.save 

    assert !ale.destroy 
    end 
end 
1

para el seguimiento de la actividad del usuario (ABM), he creado una clase hereda de registrador, y ahora estoy planeando escribir un plugin Litle para el seguimiento de usuario que puedo usar para cualquier aplicación ROR construida. Ya he comprobado si hay un complemento así, pero no lo vi. Supongo que hay muchas gemas como paper-trail, acts_as_audited o itslog, pero prefiero usar un plugin. ¿Alguna sugerencia? Aquí hay un enlace que te pueden ayudar: http://robaldred.co.uk/2009/01/custom-log-files-for-your-ruby-on-rails-applications/comment-page-1/#comment-342

agradable codificación

Cuestiones relacionadas