Накладные расходы на регистратор Голанга, если он установлен на сброс - PullRequest
1 голос
/ 20 апреля 2019

У меня есть обработчик HTTP с 40 регистраторами, который установлен на os.Stdout .

Это прекрасно работает для меня, так как я сейчас единственный, кто тестирует. Но, когда дело доходит до производства, я боюсь, что у него будет слишком много накладных расходов.

В настоящее время регистраторы настроены на os.Stdout и os.Stderr . Но как только он попадет в производство, os.Stdout будет установлен в ioutil.discard .

Q1. Повлияет ли это на производительность, если у меня все еще есть журнал, настроенный на сброс?

Q2. Для лучшей практики, лучше ли полностью удалить регистраторы из обработчика HTTP?

---- ОБНОВЛЕНИЕ ----

package main

import (
    "time"
    "fmt"
    "log"
    "io/ioutil"
    "io"
)

func testPrintf(w io.Writer, note string, cnt int) {
    l := log.New(w, "", 0)
    t1 := time.Now()
    for i:=0; i<cnt; i++ {
        l.Printf("%s - %d", "test", i)
    }
    t2 := time.Now()
    fmt.Printf("%-15s  %-15s, %v\n","Printf ", note, t2.Sub(t1))
}

func testPrintln(w io.Writer, note string, cnt int) {
    l := log.New(w, "", 0)
    t1 := time.Now()
    for i:=0; i<cnt; i++ {
        l.Println("test" + string(i))
    }
    t2 := time.Now()
    fmt.Printf("%-15s  %-15s, %v\n","Println", note, t2.Sub(t1))
}

func testDoNothing(w io.Writer, note string, cnt int) {
    //l := log.New(w, "", 0)
    t1 := time.Now()
    for i:=0; i<cnt; i++ {
        _ = "test" + string(i) // evaluated but didn't do any.
    }
    t2 := time.Now()
    fmt.Printf("%-15s  %-15s, %v\n", "DoNothing", note, t2.Sub(t1))
}

func main() {
    cnt := 10000000 // ten million
    testPrintf(ioutil.Discard, "discard.Attempt.1", cnt)
    testPrintln(ioutil.Discard, "discard.Attempt.1", cnt)
    testDoNothing(ioutil.Discard, "discard.Attempt.1", cnt)
    fmt.Println("\n")
    testPrintf(ioutil.Discard, "discard.Attempt.2", cnt)
    testPrintln(ioutil.Discard, "discard.Attempt.2", cnt)
    testDoNothing(ioutil.Discard, "discard.Attempt.2", cnt)
    fmt.Println("\n")
    testPrintf(ioutil.Discard, "discard.Attempt.3", cnt)
    testPrintln(ioutil.Discard, "discard.Attempt.3", cnt)
    testDoNothing(ioutil.Discard, "discard.Attempt.3", cnt)
}

--- РЕЗУЛЬТАТ ---

Printf           discard.Attempt.1, 2.663697209s
Println          discard.Attempt.1, 2.4289759s
DoNothing        discard.Attempt.1, 190.480694ms

Printf           discard.Attempt.2, 2.493506245s
Println          discard.Attempt.2, 2.426081786s
DoNothing        discard.Attempt.2, 182.899574ms

Printf           discard.Attempt.3, 2.480853275s
Println          discard.Attempt.3, 2.481552836s
DoNothing        discard.Attempt.3, 180.916608ms
  1. Я бегал по 10 миллионов раз для каждого.
  2. 2 ~ 3 сек. Для 10M журнала на io. Дискард быстрее, чем я думал .. Думаю, мне не нужно беспокоиться о скорости.
  3. os.Stdout, который я не собирался использовать; (Моей первоначальной задачей было сохранение кода с помощью ioutil.Discard против удаления кода), но, поскольку os.Stdout не был буферизован, это было медленно.
  4. Кстати, printf () был намного быстрее, чем я думал. Почти так же, как println ()

Я не пишу код Go каждый день, поэтому этот тест может быть неточным. Если вы видите вводящую в заблуждение информацию из теста, пожалуйста, оставьте комментарий здесь, чтобы другие знали. Спасибо.

1 Ответ

2 голосов
/ 21 апреля 2019

Тесты

Хотя моя интуиция говорит, что сброс журналов до Discard окажет минимальное влияние на ваш код, я бы порекомендовал установить эталонный тест для тестирования влияния производительности ваших регистраторов.

К счастью, Go делает это очень легко, написав функцию func BenchmarkXxxxx(*testing.B) в ваших модульных тестах и ​​запустив go test -bench. Более подробную информацию можно найти в документации . Для вашего теста я рекомендую написать два теста, один с os.Stdout, а другой с ioutil.Discard - убедитесь, что входные данные для обоих тестов одинаковы.

Соответствующий код из ioutil, чтобы показать, что произойдет «под капотом»:

// ioutil.go
type devNull int

func (devNull) Write(p []byte) (int, error) {
    return len(p), nil
}

func (devNull) WriteString(s string) (int, error) {
    return len(s), nil
}

Регистрация лучших практик

Один из подходов к ведению журнала - это обычное ведение журнала на основе уровня, при котором вы выбираете серьезность сообщения, которое хотите зарегистрировать (DEBUG / INFO / WARN / ERROR и т. Д.), Затем выбираете, какие уровни для отображения при развертывании приложения (например, DEBUG и выше в разработке, но WARN и выше в производстве).

Как вы заметили, Go предоставляет в стандартной библиотеке только операторы уровня Print и Error в стандартной библиотеке, поэтому вам потребуется внешний пакет , чтобы извлечь выгоду из чего-то подобного (это также может помочь структурировать ваш регистрирует в более удобном для чтения / поиска формате JSON). Дейв Чейни обосновал обоснование этого решения в своем блоге .

В вашем случае 40 операторов журнала звучат как много, но это может зависеть от того, сколько простых Print журналов; размер и сложность обработчика и т. д. Print регистрация может быть исключительно полезной в качестве временной меры при разработке и Error для диагностики при сбоях, если вы не обнаруживаете ошибку для клиента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...