2012-10-24 11 views
10

Występuje problem, który, moim zdaniem, musi być problemem dla większości użytkowników rails, ale nie mogłem znaleźć jeszcze na to rozwiązanie.Jak wyłączyć rejestrowanie ActiveRecord dla określonej kolumny?

Kiedy, na przykład, przesyłając plik potencjalnie dużego, binarnego pliku i przechowując go w bazie danych, z całą pewnością nie chcesz, aby szyny lub ActiveRecord logowały to konkretne pole w trybie programowania (plik dziennika, standardowe wyjście). W przypadku dość dużego pliku powoduje to przerwanie wykonywania zapytania i prawie zabicie mojego terminala.

Czy istnieje niezawodny i non-hacky metoda wyłączenia zalogowaniu do poszczególnych dziedzin? Pamiętaj, że jestem nie mówi o wyłączaniu rejestrowania dla parametrów żądania - zostało to dość dobrze rozwiązane.

Dzięki za wszelkie informacje na ten temat!

+0

Istnieje opcja filtrowania podobnie jak hasła, nie wiem, czy to by pomogło. Czy uważasz, że nadpisywanie ActiveRecord może być zhakowane? jeśli nie, to twoja podpowiedź. –

Odpowiedz

0

Nie mogę znaleźć dużo na ten temat albo, chociaż jedno można zrobić, to

ActiveRecord::Base.logger = nil 

wyłączyć zalogowaniu całkowicie, chociaż to prawdopodobnie nie chcą tego robić. Lepszym rozwiązaniem może być ustawienie rejestratora ActiveRecord na niestandardową podklasę, która nie loguje wiadomości o określonym rozmiarze, lub wykonanie czegoś inteligentniejszego w celu przeanalizowania określonych sekcji zbyt dużego komunikatu.

Nie wydaje idealne, ale to nie wydaje się realne rozwiązanie, choć nie wyglądał na konkretnych szczegółów implementacyjnych. Byłbym bardzo zainteresowany usłyszeniem lepszych rozwiązań.

+0

Tak, jest to jedyna możliwość, która przyszła mi do głowy, ale uważam, że to rozwiązanie jest dość brzydkie (jak wspomniałeś), ponieważ zdecydowanie chcesz, aby wywołanie było rejestrowane, ale po prostu nie to pole. Jeśli nie będzie "idealnej" odpowiedzi, mogę pójść do przodu i zaimplementować taki niestandardowy rejestrator, który analizuje logi ... również brzydko. – Remo

+0

Wygląda na to, że chcesz zastąpić metodę "dodaj" w programie Logger, wersja z podklasą może wykonać analizę składni, a następnie wywołać metodę klasy nadklasy, przekazując przeanalizowaną wiadomość i inne parametry. –

4

UWAGA: Działa z szyn 3, ale najwyraźniej nie 4 (która nie została wydana, gdy kwestia ta została wysłuchana)

W pliku environment.rb:

config.filter_parameters << :parameter_name 

To usunie że atrybut z wyświetlania w dziennikach, zastępując go [FILTERED] Powszechnym przypadkiem użycia dla filtrowania parametrów są oczywiście hasła, ale nie widzę powodu, dla którego nie powinien działać z twoim plikiem binarnym.

+1

Czy to nie powoduje filtrowania parametrów żądania HTTP, a nie parametrów w dzienniku SQL? – Remo

+0

Nie, filtruje wszystko w logach szyn. Zarówno parametry mieszania żądań przychodzących, jak i zalogowanych instrukcji SQL będą wyświetlać [FILTERED] – agmin

+0

Dziękujemy! Spróbuję tego i zgłoś się jak najszybciej. – Remo

5

Utwórz plik w config/inicjalizatorów whitch modyfikuje ActiveRecord::ConnectionAdapters::AbstractAdapter jak tak:

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 

Będzie trunkate wszystkich pól, które są dłuższe niż 20 znaków w dzienniku wyjściowym.

+0

Należy również pamiętać, że spowoduje to tylko skrócenie żądań wstawiania. w przypadku aktualizacji wszystko jest wrzucane do sql param, więc również musi zostać obcięte. – Patrik

+0

zła liczba argumentów (3 za 2) w wywołaniu "log_without_trunkate" w Railsach 3.0.9. – CHsurfer

+0

Metoda dziennika dla szyn 3.0.x używa metody dziennika, która pobiera tylko dwa argumenty: "sql" i "name". więc usuń wiązania i blok z argumentu i po prostu odfiltruj z argumentu sql. – Patrik

0

I napotkał ten sam problem, ale nie mogłem zorientować się czystą rozwiązanie problemu. Skończyłem na pisaniu a custom formatter dla logi Railsów, które odfiltrowywały blob.

Powyższy kod należy umieścić w config/initializers i zastąpić file_dat a kolumną, którą chcesz usunąć, i file_name z kolumną, która pojawia się w wyrażeniu regularnym.

3

Oto wdrożenie podejścia zaproponowanego przez @Patrik który pracuje dla obu wkładek i aktualizacji przeciwko PostgreSQL. Wykonywanie regexów może wymagać modyfikacji w zależności od formatowania SQL dla innych baz danych.

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 

Nie jestem z tego powodu szczęśliwy, ale na razie wystarczająco dobrze.Zachowuje pierwszy i ostatni 10 bajtów ciągu binarnego i wskazuje, ile bajtów/znaków zostało usuniętych ze środka. Nie ulega on redakcji, chyba że zredagowany tekst jest dłuższy niż tekst zastępujący (tzn. Jeśli nie ma co najmniej 20 znaków do usunięcia, wówczas "[ZMNIEJSZONE znaki xx"] będzie dłuższy niż tekst zastąpiony, więc nie ma sensu) . Nie testowałem wydajności, aby stwierdzić, czy użycie chciwego lub leniwego powtórzenia dla uśrednionego fragmentu było szybsze. Mój instynkt był leniwy, więc zrobiłem, ale jest możliwe, że chciwość byłaby szybsza, szczególnie jeśli w SQL jest tylko jedno pole binarne.

+0

IMHO. To najlepsze rozwiązanie w tym pytaniu. – reto

+0

To jest wyrażenie regularne dla mysql. Czy chcesz dodać to do swojego rozwiązania?/(? <= x '[0-9a-f] {20}) [0-9a-f] {20,}? (?= [0-9a-f] {20} ')/ – reto

6

Jeśli to pomoże każdemu, oto wersja powyższego urywka zgodna z Rails 4.1, która obejmuje również redakcję nie binarnych parametrów wiązania (na przykład kolumnę tekstową lub json) i zwiększa rejestrowanie do 100 znaków przed redakcją. Dzięki za pomoc wszystkich tutaj!

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

To zdecydowanie działa do Rails 4.2, ale nie działa w wersji 5.0+. Jak najlepiej mogę powiedzieć, nie jest to potrzebne w Railsach 5, ponieważ Railsy wydają się mieć wbudowaną podobną funkcjonalność. – Ritchie

+1

@Ritchie co jest wbudowane w funkcjonalność? – abonec

0

Oto wersja Rails 5. Po wyjęciu z pudełka Railsy 5 przycinają dane binarne, ale nie długie kolumny tekstowe.

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

w szynach 5 można umieścić go w inicjalizatorze:

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 

filtra atrybutów geo_data, value i timeline na przykład.

Powiązane problemy