Come scrivere il registro su file


108

Sto cercando di scrivere su un file di registro con Go.

Ho provato diversi approcci, tutti falliti. Questo è quello che ho provato:

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
}

Il file di registro viene creato, ma nulla viene mai stampato o aggiunto ad esso. Perché?


2
Se distribuisci il tuo programma in Linux, puoi semplicemente scrivere il tuo log nell'output std, quindi reindirizzare l'output in un file come: ./program 2> & 1 | tee logs.txt . Ci deve essere un altro modo in un altro sistema.
nvcnvn

Risposte:


165

os.Open() deve aver funzionato diversamente in passato, ma per me funziona:

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

In base ai documenti Go, os.Open()non può funzionare per log.SetOutput, perché apre il file "in lettura:"

func Open

func Open(name string) (file *File, err error) Openapre il file denominato per la lettura. In caso di successo, i metodi sul file restituito possono essere utilizzati per la lettura; il descrittore di file associato ha modalità O_RDONLY. Se c'è un errore, sarà di tipo *PathError.

MODIFICARE

Spostato defer f.Close()a dopo il if err != nilcontrollo


9
Non rimandare la chiusura prima di aver verificato se err per zero!
Volker

Non è un'attività effettivamente dannosa chiudere in tutti i casi iirc. Questo non è vero per tutti i tipi, però.
Dustin

2
@Dustin fpotrebbe essere nil, il che provocherebbe il panico. Quindi errè consigliabile controllare prima di rinviare la chiamata.
nemo

@AllisonA cura di spiegare perché Opennon funziona log.SetOutput?
nemo

1
Le autorizzazioni più sicure sono 0644 o anche 0664 per consentire la lettura / scrittura dell'utente, la lettura / scrittura dell'utente e del gruppo e in entrambi i casi non consentire la scrittura a tutti.
Jonathan

39

Preferisco la semplicità e la flessibilità della raccomandazione dell'app a 12 fattori per la registrazione. Per aggiungere a un file di registro è possibile utilizzare il reindirizzamento della shell. Il logger predefinito in Go scrive in stderr (2).

./app 2>> logfile

Vedi anche: http://12factor.net/logs


non sarà una buona pratica quando vuoi demonizzare le cose, specialmente con start-tsop-daemon
Shrey

3
@Shrey Systemd potrebbe facilmente occuparsi della registrazione e delle funzioni start-stop.
WarGasm

Nonostante questa sia una buona pratica o meno, questo è il tipo di disboscamento che stavo cercando a Golang. Grazie per aver condiviso questo!
dipendente il

C'è qualcosa di simile sotto le finestre?
surfmuggle

Era come $ 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' me NON funzionavaUbuntu 18.04.3
Ryosuke Hujisawa il

20

Di solito stampo i log sullo schermo e scrivo anche in un file. Spero che questo aiuti qualcuno.

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

7

Questo funziona per me

  1. ha creato un pacchetto chiamato 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. importa il pacchetto ovunque tu voglia accedere, ad esempio 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()))
      
      }

6

Se esegui binario su una macchina Linux potresti usare script di shell.

sovrascrivi in ​​un file

./binaryapp > binaryapp.log

aggiungere in un file

./binaryapp >> binaryapp.log

sovrascrivi stderr in un file

./binaryapp &> binaryapp.error.log

aggiungere stderr in un file

./binaryapp &>> binalyapp.error.log

può essere più dinamico utilizzando il file di script della shell.


Bello sapere, come si sovrascrive stderr per log.
impossibile il

5

Il logger predefinito in Go scrive in stderr (2). reindirizza al file

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 */

}

5

Dichiara in alto nel tuo globale in varmodo che tutti i tuoi processi possano accedere se necessario.

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

Ehi @CostaHuang, per favore lascia un feedback dettagliato. Grazie
openwonk

@CostaHuang, ho appena eseguito lo snippet di codice e funziona.
openwonk

Ciao @openwonk, ho provato di nuovo e non ha funzionato sul mio computer. La mia versione è go version go1.10.2 windows/amd64, qual è la tua?
Costa Huang

@CostaHuang, ho appena eseguito l'esempio con la tua stessa configurazione. L'esempio presuppone che tu abbia già impostato una struttura di cartelle. Ci sono modi semplici per verificarlo, tuttavia il mio obiettivo con l'esempio è mostrare quanto sia relativamente semplice la scrittura in un file di registro. Cambia il tuo codice in outfile, _ = os.Create("my.log")e funzionerà come previsto.
openwonk

Il tuo codice funziona. Stavo usando outfile, _ = os.Create("./path/to/my.log"). In qualche modo mi aspettavo che il codice avrebbe creato le path/tocartelle e il my.logfile, ma a quanto pare non ha funzionato. Suggerirei di modificare la tua risposta in modo che sia outfile, _ = os.Create("./my.log"). In questo modo sappiamo chiaramente che sta creando un registro nella cartella corrente.
Costa Huang

5

Basandomi sulla risposta di Allison e Deepak, ho iniziato a usare logrus e mi piace molto:

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

Ho un differimento f.Close () nella funzione principale


0

Sto scrivendo i registri sui file, che vengono generati su base giornaliera (al giorno viene generato un file di registro). Questo approccio funziona bene per me:

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
}

Metodo "shortenFilePath ()" utilizzato per ottenere il nome del file dal percorso completo del file. e il metodo "LogServer ()" viene utilizzato per creare un'istruzione di registro formattata (contiene: nome file, numero di riga, livello di registro, dichiarazione di errore ecc ...)


0

Per aiutare gli altri, creo una funzione di log di base per gestire la registrazione in entrambi i casi, se vuoi che l'output sia stdout, quindi attiva il debug, è semplice fare un flag di commutazione in modo da poter scegliere il tuo output.

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




0

forse questo ti aiuterà (se il file di log esiste usalo, se non esiste crealo):

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

Per maggiori dettagli: https://su9.co/9BAE74B

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.