Cara menulis log ke file


108

Saya mencoba menulis ke file log dengan Go.

Saya telah mencoba beberapa pendekatan, yang semuanya gagal. Inilah yang telah saya coba:

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
}

File log dibuat, tetapi tidak ada yang dicetak atau ditambahkan ke dalamnya. Mengapa?


2
Jika Anda menerapkan program Anda di Linux, Anda cukup menulis log Anda ke output std kemudian menyalurkan output ke file seperti: ./program 2> & 1 | tee logs.txt . Harus ada cara lain di sistem lain.
nvcnvn

Jawaban:


165

os.Open() pasti bekerja secara berbeda di masa lalu, tetapi ini berhasil untuk saya:

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

Berdasarkan dokumen Go, os.Open()tidak bisa berfungsi untuklog.SetOutput , karena membuka file "untuk membaca:"

func Open

func Open(name string) (file *File, err error) Openmembuka file bernama untuk membaca. Jika berhasil, metode pada file yang dikembalikan dapat digunakan untuk membaca; deskriptor file terkait memiliki mode O_RDONLY. Jika ada kesalahan, itu akan menjadi tipe *PathError.

EDIT

Pindah defer f.Close()ke setelah if err != nilcek


9
Jangan menunda Tutup sebelum memeriksa err karena nihil!
Volker

Sebenarnya bukan aktivitas yang berbahaya untuk ditutup dalam semua kasus iirc. Itu tidak benar untuk semua tipe.
Dustin

2
@Dustin fmungkin nil, yang akan menyebabkan kepanikan. Jadi errdisarankan untuk memeriksa sebelum menunda panggilan.
nemo

@AllisonA peduli untuk menjelaskan mengapa Opentidak bekerja dengan log.SetOutput?
nemo

1
Izin yang lebih aman adalah 0644 atau bahkan 0664 untuk mengizinkan pengguna membaca / menulis, membaca / menulis pengguna dan grup, dan dalam kedua kasus tersebut melarang semua orang menulis.
Jonathan

39

Saya lebih suka kesederhanaan dan fleksibilitas dari rekomendasi aplikasi 12 faktor untuk logging. Untuk menambahkan ke file log Anda dapat menggunakan pengalihan shell. Logger default di Go menulis ke stderr (2).

./app 2>> logfile

Lihat juga: http://12factor.net/logs


tidak akan menjadi praktik yang baik ketika Anda ingin membuat daemonisasi, terutama dengan start-tsop-daemon
Shrey

3
@Shrey Systemd dapat dengan mudah menangani logging, serta tentang fungsi start-stop.
WarGasm

Meskipun ini adalah praktik yang baik atau tidak, ini adalah jenis penebangan yang selama ini saya cari di Golang. Terima kasih telah membagikan ini!
kecanduan

Apakah ada yang serupa di bawah jendela?
surfmuggle

Apakah seperti $ 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' Saya TIDAK bekerjaUbuntu 18.04.3
Ryosuke Hujisawa

20

Saya biasanya mencetak log di layar dan menulis ke dalam file juga. Semoga ini bisa membantu seseorang.

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

Ini berhasil untuk saya

  1. membuat paket bernama 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. impor paket ke mana pun Anda ingin masuk, misalnya 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

Jika Anda menjalankan biner di mesin linux, Anda dapat menggunakan skrip shell.

menimpa menjadi file

./binaryapp > binaryapp.log

menambahkan ke dalam file

./binaryapp >> binaryapp.log

menimpa stderr menjadi file

./binaryapp &> binaryapp.error.log

menambahkan stderr ke dalam file

./binaryapp &>> binalyapp.error.log

itu bisa lebih dinamis menggunakan file skrip shell.


Senang mengetahui, bagaimana kita mengganti stderr ke log.
tidak mungkin

5

Logger default di Go menulis ke stderr (2). alihkan ke 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

Deklarasikan top di global Anda varsehingga semua proses Anda dapat mengakses jika diperlukan.

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

Hai @CostaHuang, silakan tinggalkan masukan mendetail. Terima kasih
openwonk

@CostaHuang, Saya baru saja menjalankan cuplikan kode saya dan berhasil.
openwonk

Hai @openwonk, saya telah menguji lagi dan tidak berhasil di komputer saya. Versi saya adalah go version go1.10.2 windows/amd64, apa milik Anda?
Costa Huang

@CostaHuang, saya baru saja menjalankan contoh dengan pengaturan yang sama seperti Anda. Contoh ini mengasumsikan bahwa Anda sudah menyiapkan struktur folder. Ada cara mudah untuk memeriksa ini, namun tujuan saya dengan contoh adalah untuk menunjukkan betapa relatif sederhana menulis ke file log. Ubah kode Anda menjadi outfile, _ = os.Create("my.log")dan itu akan berfungsi seperti yang diharapkan.
openwonk

Kode Anda berfungsi. Saya menggunakan outfile, _ = os.Create("./path/to/my.log"). Entah bagaimana saya memiliki harapan bahwa kode tersebut akan membuat path/tofolder dan my.logfile, tetapi ternyata itu tidak berhasil. Saya menyarankan agar Anda mengubah jawaban Anda menjadi outfile, _ = os.Create("./my.log"). Dengan cara itu kita tahu dengan jelas bahwa itu membuat log di folder saat ini.
Costa Huang

5

Berdasarkan jawaban Allison dan Deepak, saya mulai menggunakan logrus dan sangat menyukainya:

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

Saya memiliki penundaan f.Close () di fungsi utama


0

Saya menulis log ke file, yang dihasilkan setiap hari (per hari satu file log dibuat). Pendekatan ini bekerja dengan baik untuk saya:

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
}

Metode "shortenFilePath ()" digunakan untuk mendapatkan nama file dari jalur lengkap file. dan metode "LogServer ()" digunakan untuk membuat pernyataan log yang diformat (berisi: nama file, nomor baris, level log, pernyataan kesalahan, dll ...)


0

Untuk membantu orang lain, saya membuat fungsi log dasar untuk menangani logging dalam kedua kasus, jika Anda ingin output ke stdout, kemudian aktifkan debug, langsung saja lakukan flag switch sehingga Anda dapat memilih output Anda.

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

mungkin ini akan membantu Anda (jika file log ada, gunakan, jika tidak ada, buatlah):

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

Untuk detail lebih lanjut: https://su9.co/9BAE74B

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.