2012-10-24 10 views
10

Sto incontrando un problema che, a mio parere, deve essere un problema per la maggior parte degli utenti di binari ma non ho ancora trovato alcuna soluzione.Come disabilitare la registrazione di ActiveRecord per una determinata colonna?

Quando, ad esempio, si esegue un caricamento di file di un file binario potenzialmente grande e lo si memorizza nel database, non si desidera che le rotaie o ActiveRecord registrino questo campo specifico in modalità sviluppo (file di registro, stdout). Nel caso di un file abbastanza grande, questo causa l'interruzione dell'esecuzione della query e quasi uccide il mio terminale.

Esiste un metodo affidabile e non di hacking per disabilitare la registrazione per campi particolari? Ricorda, io sono non parlando di disabilitare la registrazione per i parametri di richiesta - questo è stato risolto abbastanza bene.

Grazie per qualsiasi informazione al riguardo!

+0

C'è un'opzione per filtrare le password simili, non so se questo sarebbe di aiuto. Prenderesti in considerazione la possibilità di eseguire l'override di ActiveRecord come hacky? se no, allora c'è il tuo suggerimento. –

risposta

0

non ho trovato molto su questo uno, anche se una cosa che potreste fare è

ActiveRecord::Base.logger = nil 

Per disattivare la registrazione del tutto, anche se si sarebbe probabilmente non vuole farlo. Una soluzione migliore potrebbe essere quella di impostare il logger di ActiveRecord su una sottoclasse personalizzata che non registra i messaggi su una certa dimensione, o fa qualcosa di più intelligente per analizzare sezioni specifiche di un messaggio che sono troppo grandi.

Questo non sembra ideale, ma sembra una soluzione praticabile, sebbene non abbia esaminato i dettagli di implementazione specifici. Sarei davvero interessato a conoscere le soluzioni migliori.

+0

Sì, questa è l'unica possibilità che mi è venuta in mente, ma trovo questa soluzione abbastanza brutta (come lei ha menzionato) dal momento che si desidera che la chiamata venga registrata ma non solo in questo campo. Se non ci sarà una risposta "perfetta" potrei andare avanti e implementare un tale logger personalizzato che analizza l'istruzione del registro ... brutto pure. – Remo

+0

Sembra che si desideri sovrascrivere il metodo "aggiungi" su Logger, la versione sottoclasse può eseguire l'analisi e quindi chiamare il metodo di aggiunta della superclasse, passando il messaggio analizzato e altri parametri. –

4

NOTA: Funziona con rotaie 3, ma a quanto pare non 4 (che non è stato rilasciato quando la questione è stata risolta)

Nel file application.rb:

config.filter_parameters << :parameter_name 

Questo rimuoverà che attributo da visualizzare nei registri, sostituendolo con [FILTERED] Il caso di utilizzo comune per i parametri di filtro è ovviamente password, ma non vedo alcun motivo per cui non dovrebbe funzionare con il campo del file binario.

+1

Questo non filtra semplicemente i parametri di richiesta HTTP piuttosto che i parametri nel log SQL? – Remo

+0

No, filtra tutto nei registri dei binari. Sia l'hash dei parametri che le richieste in entrata e le istruzioni SQL registrate mostreranno [FILTRATO] – agmin

+0

Grazie! Ci proverò e ti ricontatteremo al più presto. – Remo

5

creare un file in config/inizializzatori tuute modifica ActiveRecord::ConnectionAdapters::AbstractAdapter in questo modo:

class ActiveRecord::ConnectionAdapters::AbstractAdapter 
    protected 

    def log_with_trunkate(sql, name="SQL", binds=[], &block) 
    b = binds.map {|k,v| 
     v = v.truncate(20) if v.is_a? String and v.size > 20 
     [k,v] 
    } 
    log_without_trunkate(sql, name, b, &block) 
    end 

    alias_method_chain :log, :trunkate 
end 

Ciò trunkate tutti i campi che sono più di 20 caratteri nel registro di output.

+0

Dovrebbe anche notare che questo troncherà solo le richieste di inserimento. in caso di aggiornamento viene inserito tutto in sql param in modo che anche questo debba essere troncato. – Patrik

+0

numero errato di argomenti (3 per 2) nella chiamata 'log_without_trunkate' in Rails 3.0.9. – CHsurfer

+0

Il metodo di registrazione per le rotaie 3.0.x utilizza un metodo di registrazione che accetta solo due argomenti "sql" e "nome". quindi rimuovi bind e il blocco dall'argomento e filtra solo dall'argomento sql. – Patrik

0

Ho riscontrato lo stesso problema, ma non sono riuscito a trovare una soluzione pulita al problema. Ho finito per scrivere a custom formatter per il logger Rails che filtra il blob.

Il codice sopra riportato deve essere inserito in config/inizializzatori e sostituire file_dat a con la colonna che si desidera rimuovere e file_name con la colonna visualizzata dopo nell'espressione regolare.

3

Ecco un'attuazione dell'approccio suggerito da @Patrik che funziona sia per gli inserimenti che per gli aggiornamenti rispetto a PostgreSQL. Potrebbe essere necessario regolare la regex a seconda della formattazione di SQL per altri database.

class ActiveRecord::ConnectionAdapters::AbstractAdapter 
    protected 

    def log_with_binary_truncate(sql, name="SQL", binds=[], &block) 
    binds = binds.map do |col, data| 
     if col.type == :binary && data.is_a?(String) && data.size > 27 
     data = "#{data[0,10]}[REDACTED #{data.size - 20} bytes]#{data[-10,10]}" 
     end 
     [col, data] 
    end 

    sql = sql.gsub(/(?<='\\x[0-9a-f]{20})[0-9a-f]{20,}?(?=[0-9a-f]{20}')/) do |match| 
     "[REDACTED #{match.size} chars]" 
    end 

    log_without_binary_truncate(sql, name, binds, &block) 
    end 

    alias_method_chain :log, :binary_truncate 
end 

Non ne sono delirantemente soddisfatto, ma per ora è abbastanza buono.Conserva il primo e l'ultimo 10 byte della stringa binaria e indica quanti byte/caratteri sono stati rimossi dal centro. Non viene redatta a meno che il testo redatto sia più lungo del testo sostitutivo (cioè se non ci sono almeno 20 caratteri da rimuovere, quindi "[REDATED xx chars]" sarà più lungo del testo sostituito, quindi non c'è alcun punto) . Non ho fatto test delle prestazioni per determinare se l'utilizzo della ripetizione avida o pigra per il blocco redatto fosse più veloce. Il mio istinto era quello di diventare pigro, così ho fatto, ma è possibile che avido sarebbe più veloce soprattutto se c'è un solo campo binario in SQL.

+0

IMHO. Questa è la migliore soluzione in questa domanda. – reto

+0

Questa è l'espressione regolare per mysql. Vuoi aggiungerlo alla tua soluzione?/(? <= X '[0-9a-f] {20}) [0-9a-f] {20,}? (?= [0-9a-f] {20} ')/ – reto

6

Se questo aiuta qualcuno, ecco una versione compatibile con Rails 4.1 dello snippet precedente che include anche la redazione di parametri di binding non binari (ad esempio una colonna di testo o json) e aumenta la registrazione a 100 caratteri prima della redazione. Grazie per l'aiuto di tutti qui!

class ActiveRecord::ConnectionAdapters::AbstractAdapter 
    protected 

    def log_with_binary_truncate(sql, name="SQL", binds=[], statement_name = nil, &block) 
    binds = binds.map do |col, data| 
     if data.is_a?(String) && data.size > 100 
     data = "#{data[0,10]} [REDACTED #{data.size - 20} bytes] #{data[-10,10]}" 
     end 
     [col, data] 
    end 

    sql = sql.gsub(/(?<='\\x[0-9a-f]{100})[0-9a-f]{100,}?(?=[0-9a-f]{100}')/) do |match| 
     "[REDACTED #{match.size} chars]" 
    end 

    log_without_binary_truncate(sql, name, binds, statement_name, &block) 
    end 

    alias_method_chain :log, :binary_truncate 
end 
+1

Funziona sicuramente su Rails 4.2 ma non funziona in 5.0+. Come meglio posso dire, non è necessario in Rails 5 perché Rails sembra avere funzionalità simili incorporate. – Ritchie

+1

@Ritchie cosa è incorporato nella funzionalità? – abonec

0

Ecco una versione di Rails 5. Rails 5 consente di troncare i dati binari, ma non le colonne di testo lunghe.

module LogTruncater 
    def render_bind(attribute) 
    num_chars = Integer(ENV['ACTIVERECORD_SQL_LOG_MAX_VALUE']) rescue 120 
    half_num_chars = num_chars/2 
    value = if attribute.type.binary? && attribute.value 
     if attribute.value.is_a?(Hash) 
     "<#{attribute.value_for_database.to_s.bytesize} bytes of binary data>" 
     else 
     "<#{attribute.value.bytesize} bytes of binary data>" 
     end 
    else 
     attribute.value_for_database 
    end 

    if value.is_a?(String) && value.size > num_chars 
     value = "#{value[0,half_num_chars]} [REDACTED #{value.size - num_chars} chars] #{value[-half_num_chars,half_num_chars]}" 
    end 

    [attribute.name, value] 
    end 

end 

class ActiveRecord::LogSubscriber 
    prepend LogTruncater 
end 
0

In rotaie 5 si potrebbe mettere in initializer:

module SqlLogFilter 

    FILTERS = Set.new(%w(geo_data value timeline)) 
    def render_bind(attribute) 
    return [attribute.name, '<filtered>'] if FILTERS.include?(attribute.name) 
    super 
    end 

end 
ActiveRecord::LogSubscriber.prepend SqlLogFilter 

Per filtro attributi geo_data, value e timeline per esempio.

Problemi correlati