2012-05-13 11 views
24

Mam trochę mocno oprzyrządowanego kodu, który korzysta z pakietu log. Teraz nadszedł czas, aby wyłączyć rejestrowanie i nie mogę ustalić, jak wyłączyć standardowy rejestrator.Go: Wyłączyć log.Logger?

Czy coś przeoczyłem? Czy powinienem sprawdzać flagę przed wywoływaniem logów lub komentowaniem ich w produkcji?

Odpowiedz

10

Dla całkowitemu wyłączeniu dzienników, to faktycznie lepiej zadzwonić log.SetFlags(0)Joril i ustawić wyjście na no-op io.Writer (tj log.SetOutput(ioutil.Discard))

ale nawet po tym, operacje będzie pracował około 500-600 ns/op

ten można jeszcze wyciąć Sho rt (do około 100 ns/op) za pomocą niestandardowej implementacji Logger i zaimplementowanie wszystkich funkcji jako no-op - jak pokazano here (tylko przesłonięcie Println dla bervity).

Alternatywą dla tych wszystkich jest użycie niestandardowej struktury rejestrowania z poziomami i ustawienie jej do zakończenia.

Uwaga choć jeden z powszechnie stosowanych biblioteki do logowania (logrus) ma performance implications - to samo można znaleźć w benchmarks gdzie wystąpi z 3K + ns/OP niezależnie.

Stronnicze opinia: od benchmarków, biblioteki go-logging Wykonuje na równi z realizacji niestandardowych Logger przy ustalaniu Level do -1, niezależnie od backend i formatowanie

(benchmark źródło można znaleźć here)

wyjście odniesienia jest następujący:

testing: warning: no tests to run 
PASS 
BenchmarkGoLogging-4            1000000   2068 ns/op 
BenchmarkGoLoggingNullBackend-4         5000000   308 ns/op 
BenchmarkGoLoggingNullBackendWithFancyFormatter-4    3000000   435 ns/op 
BenchmarkGoLoggingOffLevel-4         20000000   109 ns/op 
BenchmarkGoLoggingNullBackendAndOffLevel-4      20000000   108 ns/op 
BenchmarkGoLoggingNullBackendWithFancyFormatterAndOffLevel-4 20000000   109 ns/op 
BenchmarkLog15-4             200000   7359 ns/op 
BenchmarkLog15WithDiscardHandler-4        2000000   922 ns/op 
BenchmarkLog15WithDiscardHandlerAndOffLevel-4     2000000   926 ns/op 
BenchmarkLog15WithNopLogger-4         20000000   108 ns/op 
BenchmarkLog15WithNopLoggerDiscardHandlerA-4     20000000   112 ns/op 
BenchmarkLog15WithNopLoggerAndDiscardHandlerAndOffLevel-4  20000000   112 ns/op 
BenchmarkLog-4             1000000   1217 ns/op 
BenchmarkLogIoDiscardWriter-4         2000000   724 ns/op 
BenchmarkLogIoDiscardWriterWithoutFlags-4      3000000   543 ns/op 
BenchmarkLogCustomNullWriter-4         2000000   731 ns/op 
BenchmarkLogCustomNullWriterWithoutFlags-4      3000000   549 ns/op 
BenchmarkNopLogger-4           20000000   113 ns/op 
BenchmarkNopLoggerWithoutFlags-4        20000000   112 ns/op 
BenchmarkLogrus-4             300000   3832 ns/op 
BenchmarkLogrusWithDiscardWriter-4        500000   3032 ns/op 
BenchmarkLogrusWithNullFormatter-4        500000   3814 ns/op 
BenchmarkLogrusWithPanicLevel-4         500000   3872 ns/op 
BenchmarkLogrusWithDiscardWriterAndPanicLevel-4     500000   3085 ns/op 
BenchmarkLogrusWithDiscardWriterAndNullFormatterAndPanicLevel-4 500000   3064 ns/op 
ok  log-benchmarks 51.378s 
go test -bench . 62.17s user 3.90s system 126% cpu 52.065 total 

# 1: YMMV, testowane na i7-4500U CPU @ 1.80GHz

5
type NullWriter int 
func (NullWriter) Write([]byte) (int, error) { return 0, nil } 

// ... 

log.SetOutput(new(NullWriter)) 
+1

Czyżby się zdarzyć, że będzie próbował kontynuować pisanie czy 'int' zwracana jest wartość' niektórzy użytkownicy Writer 0'? (może może powinniśmy zwrócić 'len (b)' gdy argumentem jest 'b [] byte'?) – Ashe

+2

Ogólnie masz rację, ale pakiet' log' [po prostu ignoruje] (http://code.google.com /p/go/source/browse/src/pkg/log/log.go#153) 'int' zwraca wartość, więc lepiej nie marnować czasu na tak częstą funkcję w kodzie produkcyjnym. – Mostafa

+2

Czy są jakieś sprawdzone metody używania 'log' w Go? Czy większość zgłoszeń rejestracyjnych jest w końcu usuniętych (lub skomentowanych), jeśli służą do celów śledzenia? –

3

Takie podejście pozwala włączyć zalogowaniu się i wyłącza przy starcie:

type LogWriter struct{ 
    enabled bool 
} 

func (l *LogWriter) Enable() { 
    l.enabled = true 
} 

func (l *LogWriter) Disable() { 
    l.enabled = false 
} 

func (l *LogWriter) Write([]byte) (int, error) { 
    if l.enabled { 
     //... 
    } 
    return 0, nil 
} 

I to podejście włącza lub wyłącza zalogowaniu na cały czas trwania:

type LogWriter struct{} 

func (l *LogWriter) Write([]byte) (int, error) { 
    if some.Constant { 
     //... 
    } 
    return 0, nil 
} 

Gdzie some.Constant gdyby tworzyły być stałą, którą ustawiłeś przed kompilacją (tworząc plik binarny "produkcja" "produkcja") możliwość, która jest ustawiona tylko jeden raz podczas uruchamiania programu za pomocą flag linii poleceń (podobnie jak myprogram --enable-logging=true)

Przy obu podejściach możesz pozostawić swój obecny kod prawie całkowicie nietknięty.

84

Nie ma powodu, aby utworzyć własny typ dla wspólnego obiektu io.Writer, jeśli taki istnieje w pakiecie io/ioutil.

import (
    "log" 
    "io/ioutil" 
) 

func init() { 
    log.SetOutput(ioutil.Discard) 
} 
+0

Świetne znalezisko! Szukałem czegoś takiego w standardowych bibliotekach, ale nie sprawdzałem 'io/ioutil'. – Mostafa

+0

Czy to wyłączenie rejestratora tylko dla pakietu lub całej aplikacji? – Kiril

+1

Skoro to pomija zapisywanie na dysk, ale nadal wykonuje całe formatowanie, wywołanie 'log.SetFlags (0)' powinno nieco złagodzić to, myślę, że – Joril

1

Uwaga dla innych nadchodzących tutaj szukając tego i innych obiektów rejestrowania: przyjrzeć się opakowaniu log4go jako że obejmuje wyłączenie rejestrowania, ustawienie poziomu dziennika dziennika rotacji, przekierowanie do pliku itp, które mogłyby być przydatny.

Zobacz doc na http://godoc.org/code.google.com/p/log4go

2

Od SetOutput() jest zdefiniowana tylko dla globalnego rejestratora, zwyczaj pisarz jest nadal przydatny dla innych rejestratorów. Krótka droga pisanie jeden jest tak:

type LogWriter struct{ io.Writer } 
func (w *LogWriter) Enable() { w.Writer = os.Stdout } 
func (w *LogWriter) Disable() { w.Writer = ioutil.Discard }