Wie schreibe ich ein Protokoll in eine Datei?

106

Ich versuche mit Go in eine Protokolldatei zu schreiben.

Ich habe verschiedene Ansätze ausprobiert, die alle fehlgeschlagen sind. Folgendes habe ich versucht:

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
}

Die Protokolldatei wird erstellt, aber es wird nie etwas gedruckt oder angehängt. Warum?

Allison A.
quelle
2
Wenn Sie Ihr Programm unter Linux bereitstellen, können Sie einfach Ihr Protokoll in die Standardausgabe schreiben und die Ausgabe dann in eine Datei wie: ./program 2> & 1 | leiten tee logs.txt . Es muss einen anderen Weg in einem anderen System geben.
nvcnvn

Antworten:

163

os.Open() muss in der Vergangenheit anders funktioniert haben, aber das funktioniert bei mir:

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")

Basierend auf den Go-Dokumenten os.Open()kann nicht gearbeitet werden log.SetOutput, da die Datei "zum Lesen:" geöffnet wird.

func Open

func Open(name string) (file *File, err error) Openöffnet die benannte Datei zum Lesen. Bei Erfolg können Methoden für die zurückgegebene Datei zum Lesen verwendet werden. Der zugehörige Dateideskriptor hat den Modus O_RDONLY. Wenn ein Fehler auftritt, ist er vom Typ *PathError.

BEARBEITEN

Nach Überprüfung defer f.Close()nach verschobenif err != nil

Allison A.
quelle
9
Verschieben Sie Close nicht, bevor Sie err auf Null prüfen!
Volker
Es ist nicht wirklich schädlich, in allen Fällen iirc zu schließen. Das gilt jedoch nicht für alle Typen.
Dustin
2
@Dustin fkönnte sein nil, was zu einer Panik führen würde. Es errist daher ratsam, vor dem Aufschieben des Anrufs nachzuschauen.
Nemo
@AllisonEine Erklärung, warum Opennicht funktioniert log.SetOutput?
Nemo
1
Die sichereren Berechtigungen sind 0644 oder sogar 0664, um das Lesen / Schreiben von Benutzern, das Lesen / Schreiben von Benutzern und Gruppen zu ermöglichen und in beiden Fällen das Schreiben von Benutzern zu untersagen.
Jonathan
37

Ich bevorzuge die Einfachheit und Flexibilität der 12-Faktor-App-Empfehlung für die Protokollierung. Zum Anhängen an eine Protokolldatei können Sie die Shell-Umleitung verwenden. Der Standardlogger in Go schreibt in stderr (2).

./app 2>> logfile

Siehe auch: http://12factor.net/logs

Philip Nelson
quelle
wird keine gute Praxis sein, wenn Sie Dinge dämonisieren wollen, besonders mit start-tsop-daemon
Shrey
3
@Shrey Systemd kann sich problemlos um die Protokollierung sowie um Start-Stopp-Funktionen kümmern.
WarGasm
Obwohl dies eine gute Praxis ist oder nicht, ist dies die Art der Protokollierung, nach der ich in Golang gesucht habe. Danke, dass du das geteilt hast!
süchtig
Gibt es etwas Ähnliches unter Fenstern?
Surfmuggle
War wie $ 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' ich NICHT arbeitenUbuntu 18.04.3
Ryosuke Hujisawa
20

Normalerweise drucke ich die Protokolle auf dem Bildschirm und schreibe auch in eine Datei. Hoffe das hilft jemandem.

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
quelle
7

Das funktioniert bei mir

  1. hat ein Paket namens logger.go erstellt

    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. Importieren Sie das Paket, wo immer Sie sich anmelden möchten, z. B. 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
quelle
6

Wenn Sie Binärdateien auf einem Linux-Computer ausführen, können Sie ein Shell-Skript verwenden.

in eine Datei überschreiben

./binaryapp > binaryapp.log

in eine Datei anhängen

./binaryapp >> binaryapp.log

Überschreiben Sie stderr in eine Datei

./binaryapp &> binaryapp.error.log

füge stderr in eine Datei ein

./binaryapp &>> binalyapp.error.log

Mit der Shell-Skriptdatei kann es dynamischer sein.

Adzimzf
quelle
Gut zu wissen, wie wir stderr überschreiben, um zu protokollieren.
unmöglich
5

Der Standardlogger in Go schreibt in stderr (2). In Datei umleiten

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
quelle
5

Deklarieren Sie sich in Ihrem globalen Bereich als varganz oben, damit alle Ihre Prozesse bei Bedarf darauf zugreifen können.

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
quelle
Hey @CostaHuang, bitte hinterlasse ein detailliertes Feedback. Danke
openwonk
@CostaHuang, ich habe gerade mein Code-Snippet ausgeführt und es funktioniert.
Openwonk
Hallo @openwonk, ich habe es erneut getestet und es hat auf meinem Computer nicht funktioniert. Meine Version ist go version go1.10.2 windows/amd64, was ist deine?
Costa Huang
@CostaHuang, ich habe gerade ein Beispiel mit der gleichen Einrichtung wie Sie ausgeführt. In diesem Beispiel wird davon ausgegangen, dass Sie bereits eine Ordnerstruktur eingerichtet haben. Es gibt einfache Möglichkeiten, dies zu überprüfen. Mein Ziel ist es jedoch, anhand eines Beispiels zu zeigen, wie relativ einfach das Schreiben in eine Protokolldatei ist. Ändern Sie Ihren Code in outfile, _ = os.Create("my.log")und es wird wie erwartet funktionieren.
Openwonk
Ihr Code funktioniert. Ich habe benutzt outfile, _ = os.Create("./path/to/my.log"). Irgendwie hatte ich die Erwartung, dass der Code die path/toOrdner und die my.logDatei erstellen wird , aber anscheinend hat es nicht funktioniert. Ich würde vorschlagen, dass Sie Ihre Antwort so ändern, dass sie lautet outfile, _ = os.Create("./my.log"). Auf diese Weise wissen wir eindeutig, dass im aktuellen Ordner ein Protokoll erstellt wird.
Costa Huang
5

Aufbauend auf der Antwort von Allison und Deepak habe ich angefangen, logrus zu verwenden und es gefällt mir wirklich:

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)
}

Ich habe eine Verschiebung f.Close () in der Hauptfunktion

PeggyScott
quelle
0

Ich schreibe Protokolle in die Dateien, die täglich generiert werden (pro Tag wird eine Protokolldatei generiert). Dieser Ansatz funktioniert gut für mich:

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
}

Die Methode "shortenFilePath ()" wird verwendet, um den Namen der Datei aus dem vollständigen Dateipfad abzurufen. und die Methode "LogServer ()" wird verwendet, um eine formatierte Protokollanweisung zu erstellen (enthält: Dateiname, Zeilennummer, Protokollebene, Fehleranweisung usw.)

Hardik Bohra
quelle
0

Um anderen zu helfen, erstelle ich eine grundlegende Protokollfunktion, um die Protokollierung in beiden Fällen zu handhaben. Wenn Sie möchten, dass die Ausgabe standardisiert wird, aktivieren Sie das Debugging. Es ist einfach, ein Switch-Flag auszuführen, damit Sie Ihre Ausgabe auswählen können.

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
quelle