ruby ruby-on-rails-3 logging observer-pattern auditing

ruby - Sistema de auditoría Rails con ActiveResource y ActiveRecord



ruby-on-rails-3 logging (6)

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

Y en lo que se refiere a ActiveResource, también será un modelo en 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.

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.

También debo tener la posibilidad de buscar cualquier dato por fecha, cualquier campo ... etc.

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

  • El usuario Bob cambia su contraseña a * y envía un correo electrónico a ** en 2011-08-12 08:12
  • Staff Jeff agregó nuevo socio: Nombre de la empresa en 2011-08-12 08:13
  • Admin Jack ha eliminado el producto: Nombre del producto en 2011-09-12 11:11
  • Cliente Sam ordenó un nuevo servicio: Nombre del servicio en 2011-09-12 11:12

¿Alguien implementa tal registro? Ideas? Consejos?

¿Debería usar gemas o puedo hacer toda la lógica con observadores que no cambian de modelo?

Me gustó gema https://github.com/airblade/paper_trail ¿alguien puede decir cómo puedo hacer que funcione con activeresource?


https://github.com/collectiveidea/acts_as_audited

y

https://github.com/airblade/paper_trail

son ambas excelentes soluciones para ActiveRecord solamente, pero dado que gran parte de ActiveRecord se ha extraído a ActiveModel , es probable que sea razonable ampliarlo para que también sea compatible con ActiveResource , 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 un 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 los aspectos internos de ninguno de los dos.


Echa un vistazo a esta transmisión de rieles, tal vez te pueda ayudar: Notificaciones



Fivell, acabo de ver esta pregunta y no tengo tiempo para arreglar alteraciones esta noche antes de que caduque la recompensa, así que le daré mi código de auditoría que funciona con ActiveRecord y debería funcionar con ActiveResource, quizás con algunos ajustes (I no use ARes con la frecuencia suficiente como para saberlo). Sé que las devoluciones de llamada que usamos están ahí, pero no estoy seguro si ARes tiene el seguimiento de changes atributos sucios de ActiveRecord.

Este código registra cada CREATE / UPDATE / DELETE en todos los modelos (excepto CREATE en el modelo de registro de auditoría y cualquier otra excepción 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 ActiveRecord que este reemplazó no funcionó en las tareas de rake o la consola.) Oh, este código proviene de una aplicación 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 tenemos que analizar un problema), pero dado que los cambios se almacenan como JSON, debería ser bastante sencillo.

Primero, almacenamos el usuario actual en User.current para que esté accesible en todas partes, por lo que en app/models/user.rb :

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

El usuario actual se configura en el controlador de la aplicación para cada solicitud de ese modo (y no causa problemas de concurrencia):

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

Puede establecer User.current en sus tareas de rake si tiene sentido hacerlo.

A continuación, definimos el modelo para almacenar la app/models/audit_log_entry.rb información de auditoría app/models/audit_log_entry.rb - usted querrá personalizar IgnoreClassesRegEx para que se ajuste a cualquier modelo que no desee auditar:

# == 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, agregamos algunos métodos a ActiveRecord::Base para que funcionen las auditorías. Deberá consultar el método audit_log_clean_backtrace y modificarlo según sus necesidades. (FWIW, agregamos 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

Finalmente, aquí están las pruebas que tenemos sobre esto: por supuesto, deberá modificar las acciones de prueba reales. 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


para rastrear la actividad del usuario (CRUD), he creado una clase que hereda de Logger, y ahora estoy planeando escribir un pequeño complemento para rastrear usuarios que pueda usar para cualquier aplicación ROR creada. 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 podría ayudarlo: http://robaldred.co.uk/2009/01/custom-log-log-files-for-your-ruby-on-rails-applications/comment-page-1/#comment- 342

buena codificación