Jak zapisać log do pliku

108

Próbuję pisać do pliku dziennika za pomocą Go.

Wypróbowałem kilka podejść, z których wszystkie zawiodły. Oto, czego próbowałem:

func TestLogging(t *testing.T) {
    if !FileExists("logfile") {
        CreateFile("logfile")
    }
    f, err := os.Open("logfile")
    if err != nil {
        t.Fatalf("error: %v", err)
    }

    // attempt #1
    log.SetOutput(io.MultiWriter(os.Stderr, f))
    log.Println("hello, logfile")

    // attempt #2
    log.SetOutput(io.Writer(f))
    log.Println("hello, logfile")

    // attempt #3
    log.SetOutput(f)
    log.Println("hello, logfile")
}

func FileExists(name string) bool {
    if _, err := os.Stat(name); err != nil {
       if os.IsNotExist(err) {
            return false
        }
    }
    return true
}

func CreateFile(name string) error {
    fo, err := os.Create(name)
    if err != nil {
        return err
    }
    defer func() {
        fo.Close()
    }()
    return nil
}

Plik dziennika zostaje utworzony, ale nic nie jest drukowane ani do niego dołączane. Czemu?

Allison A
źródło
2
Jeśli wdrażasz swój program w systemie Linux, możesz po prostu zapisać swój dziennik na standardowe wyjście, a następnie przesłać dane wyjściowe do pliku takiego jak: ./program 2> & 1 | tee logs.txt . W innym systemie musi być jakiś inny sposób.
nvcnvn

Odpowiedzi:

165

os.Open() musiało działać inaczej w przeszłości, ale to działa dla mnie:

f, err := os.OpenFile("testlogfile", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
if err != nil {
    log.Fatalf("error opening file: %v", err)
}
defer f.Close()

log.SetOutput(f)
log.Println("This is a test log entry")

Na podstawie dokumentów Go, os.Open()nie działa log.SetOutput, ponieważ otwiera plik „do czytania:”

func Open

func Open(name string) (file *File, err error) Openotwiera nazwany plik do odczytu. Jeśli się powiedzie, do odczytu można użyć metod na zwróconym pliku; powiązany deskryptor pliku ma tryb O_RDONLY. Jeśli wystąpi błąd, będzie to typ *PathError.

EDYTOWAĆ

Przeniesiony defer f.Close()do po if err != nilsprawdzeniu

Allison A
źródło
9
Nie odraczaj zamykania przed sprawdzeniem, czy w błędzie jest zero!
Volker
Zamknięcie we wszystkich przypadkach nie jest działaniem szkodliwym. Nie dotyczy to jednak wszystkich typów.
Dustin
2
@Dustin fmoże być nil, co spowoduje panikę. Dlatego errwskazane jest sprawdzenie przed odroczeniem połączenia.
nemo
@AllisonChcesz wyjaśnić, dlaczego Opennie chcesz pracować z log.SetOutput?
nemo
1
Bezpieczniejsze uprawnienia to 0644 lub nawet 0664, które pozwalają użytkownikowi na odczyt / zapis, użytkownikowi i grupie na odczyt / zapis, aw obu przypadkach uniemożliwiają każdemu pisanie.
Jonathan,
39

Wolę prostotę i elastyczność 12-czynnikowej rekomendacji aplikacji do logowania. Aby dołączyć do pliku dziennika, możesz użyć przekierowania powłoki. Domyślny program rejestrujący w Go zapisuje do stderr (2).

./app 2>> logfile

Zobacz też: http://12factor.net/logs

Philip Nelson
źródło
nie będzie dobrą praktyką, gdy chcesz demonizować rzeczy, szczególnie ze start-tsop-daemon
Shrey
3
@Shrey Systemd może łatwo zająć się logowaniem, a także funkcjami start-stop.
WarGasm
Mimo, że jest to dobra praktyka czy nie, to właśnie tego typu wycinki szukałem w Golangu. Dzięki za udostępnienie tego!
uzależniony
Czy jest coś podobnego pod oknami?
surfmuggle
Było jak $ cd /etc/systemd/system $ sudo vi app.service ExecStart=/bin/bash -c 'sudo go run main.go >> /home/ubuntu/go/src/html_menu_1/logfile' ja nie pracowałemUbuntu 18.04.3
Ryosuke Hujisawa
21

Zwykle drukuję dzienniki na ekranie i również piszę do pliku. Mam nadzieję, że to komuś pomoże.

f, err := os.OpenFile("/tmp/orders.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
    log.Fatalf("error opening file: %v", err)
}
defer f.Close()
wrt := io.MultiWriter(os.Stdout, f)
log.SetOutput(wrt)
log.Println(" Orders API Called")
deepakssn
źródło
7

To działa dla mnie

  1. utworzył pakiet o nazwie logger.go

    package logger
    
    import (
      "flag"
      "os"
      "log"
      "go/build"
    )
    
    var (
      Log      *log.Logger
    )
    
    
    func init() {
        // set location of log file
        var logpath = build.Default.GOPATH + "/src/chat/logger/info.log"
    
       flag.Parse()
       var file, err1 = os.Create(logpath)
    
       if err1 != nil {
          panic(err1)
       }
          Log = log.New(file, "", log.LstdFlags|log.Lshortfile)
          Log.Println("LogFile : " + logpath)
    }
    
    1. zaimportuj paczkę wszędzie tam, gdzie chcesz się zalogować, np. main.go

      package main
      
      import (
         "logger"
      )
      
      const (
         VERSION = "0.13"
       )
      
      func main() {
      
          // time to use our logger, print version, processID and number of running process
          logger.Log.Printf("Server v%s pid=%d started with processes: %d", VERSION, os.Getpid(),runtime.GOMAXPROCS(runtime.NumCPU()))
      
      }
      
philip mudenyo
źródło
6

Jeśli uruchamiasz plik binarny na komputerze z systemem Linux, możesz użyć skryptu powłoki.

nadpisać do pliku

./binaryapp > binaryapp.log

dołączyć do pliku

./binaryapp >> binaryapp.log

nadpisz stderr do pliku

./binaryapp &> binaryapp.error.log

dołącz stderr do pliku

./binaryapp &>> binalyapp.error.log

może być bardziej dynamiczny przy użyciu pliku skryptu powłoki.

Adzimzf
źródło
Dobrze wiedzieć, jak zastąpić stderr w logu.
niemożliwe
5

Domyślny program rejestrujący w Go zapisuje do stderr (2). przekierowanie do pliku

import ( 
    "syscall"
    "os" 
 )
func main(){
  fErr, err = os.OpenFile("Errfile", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
  syscall.Dup2(int(fErr.Fd()), 1) /* -- stdout */
  syscall.Dup2(int(fErr.Fd()), 2) /* -- stderr */

}
Sergey
źródło
5

Zadeklaruj najwyższy poziom w swoim globalnym, varaby wszystkie procesy miały dostęp w razie potrzeby.

package main

import (
    "log"
    "os"
)
var (
    outfile, _ = os.Create("path/to/my.log") // update path for your needs
    l      = log.New(outfile, "", 0)
)

func main() {
    l.Println("hello, log!!!")
}
openwonk
źródło
Hej @CostaHuang, zostaw szczegółową opinię. Dzięki
openwonk
@CostaHuang, właśnie uruchomiłem fragment kodu i działa.
openwonk
Cześć @openwonk, ponownie przetestowałem i nie działa na moim komputerze. Moja wersja to go version go1.10.2 windows/amd64, jaka jest twoja?
Costa Huang
@CostaHuang, właśnie uruchomiłem przykład z taką samą konfiguracją jak Ty. W przykładzie założono, że masz już skonfigurowaną strukturę folderów. Istnieją łatwe sposoby, aby to sprawdzić, ale moim celem na przykładzie jest pokazanie, jak stosunkowo proste jest zapisywanie do pliku dziennika. Zmień kod na outfile, _ = os.Create("my.log")i będzie działał zgodnie z oczekiwaniami.
openwonk
Twój kod działa. Używałem outfile, _ = os.Create("./path/to/my.log"). Spodziewałem się, że kod utworzy path/tofoldery i my.logplik, ale najwyraźniej nie zadziałało. Proponuję zmodyfikować swoją odpowiedź na „be” outfile, _ = os.Create("./my.log"). W ten sposób wyraźnie wiemy, że tworzy dziennik w bieżącym folderze.
Costa Huang,
5

Opierając się na odpowiedzi Allison i Deepak, zacząłem używać Logrusa i bardzo mi się podoba:

var log = logrus.New()

func init() {

    // log to console and file
    f, err := os.OpenFile("crawler.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        log.Fatalf("error opening file: %v", err)
    }
    wrt := io.MultiWriter(os.Stdout, f)

    log.SetOutput(wrt)
}

Mam odroczenie f.Close () w głównej funkcji

PeggyScott
źródło
0

Piszę logi do plików, które są generowane codziennie (dziennie generowany jest jeden plik dziennika). To podejście działa dobrze dla mnie:

var (
    serverLogger *log.Logger
)

func init() {
    // set location of log file
    date := time.Now().Format("2006-01-02")
    var logpath = os.Getenv(constant.XDirectoryPath) + constant.LogFilePath + date + constant.LogFileExtension
    os.MkdirAll(os.Getenv(constant.XDirectoryPath)+constant.LogFilePath, os.ModePerm)
    flag.Parse()
    var file, err1 = os.OpenFile(logpath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)

    if err1 != nil {
        panic(err1)
    }
    mw := io.MultiWriter(os.Stdout, file)
    serverLogger = log.New(mw, constant.Empty, log.LstdFlags)
    serverLogger.Println("LogFile : " + logpath)
}

// LogServer logs to server's log file
func LogServer(logLevel enum.LogLevel, message string) {
    _, file, no, ok := runtime.Caller(1)
    logLineData := "logger_server.go"
    if ok {
        file = shortenFilePath(file)
        logLineData = fmt.Sprintf(file + constant.ColonWithSpace + strconv.Itoa(no) + constant.HyphenWithSpace)
    }
    serverLogger.Println(logLineData + logLevel.String() + constant.HyphenWithSpace + message)
}

// ShortenFilePath Shortens file path to a/b/c/d.go tp d.go
func shortenFilePath(file string) string {
    short := file
    for i := len(file) - 1; i > 0; i-- {
        if file[i] == constant.ForwardSlash {
            short = file[i+1:]
            break
        }
    }
    file = short
    return file
}

Metoda „shortenFilePath ()” używana do pobierania nazwy pliku z pełnej ścieżki do pliku. i metoda „LogServer ()” służy do tworzenia sformatowanej instrukcji dziennika (zawiera: nazwę pliku, numer wiersza, poziom dziennika, opis błędu itp.)

Hardik Bohra
źródło
0

Aby pomóc innym, tworzę podstawową funkcję dziennika do obsługi rejestrowania w obu przypadkach, jeśli chcesz, aby wyjście było na standardowe wyjście, włącz debugowanie, po prostu wykonuję flagę przełącznika, abyś mógł wybrać wyjście.

func myLog(msg ...interface{}) {
    defer func() { r := recover(); if r != nil { fmt.Print("Error detected logging:", r) } }()
    if conf.DEBUG {
        fmt.Println(msg)
    } else {
        logfile, err := os.OpenFile(conf.LOGDIR+"/"+conf.AppName+".log", os.O_RDWR | os.O_CREATE | os.O_APPEND,0666)
        if !checkErr(err) {
            log.SetOutput(logfile)
            log.Println(msg)
        }
        defer logfile.Close()
    }
}




Cyberience
źródło
0

może to ci pomoże (jeśli plik dziennika istnieje, użyj go, jeśli nie istnieje, utwórz go):

package main

import (
    "flag"
    "log"
    "os"
)
//Se declara la variable Log. Esta será usada para registrar los eventos.
var (
    Log *log.Logger = Loggerx()
)

func Loggerx() *log.Logger {
    LOG_FILE_LOCATION := os.Getenv("LOG_FILE_LOCATION")
        //En el caso que la variable de entorno exista, el sistema usa la configuración del docker.
    if LOG_FILE_LOCATION == "" {
        LOG_FILE_LOCATION = "../logs/" + APP_NAME + ".log"
    } else {
        LOG_FILE_LOCATION = LOG_FILE_LOCATION + APP_NAME + ".log"
    }
    flag.Parse()
        //Si el archivo existe se rehusa, es decir, no elimina el archivo log y crea uno nuevo.
    if _, err := os.Stat(LOG_FILE_LOCATION); os.IsNotExist(err) {
        file, err1 := os.Create(LOG_FILE_LOCATION)
        if err1 != nil {
            panic(err1)
        }
                //si no existe,se crea uno nuevo.
        return log.New(file, "", log.Ldate|log.Ltime|log.Lshortfile)
    } else {
                //si existe se rehusa.
        file, err := os.OpenFile(LOG_FILE_LOCATION, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
        if err != nil {
            panic(err)
        }
        return log.New(file, "", log.Ldate|log.Ltime|log.Lshortfile)
    }
}

Więcej szczegółów: https://su9.co/9BAE74B

Jose G. Mejía
źródło