Cách ghi nhật ký vào tệp


108

Tôi đang cố gắng ghi vào tệp nhật ký bằng Go.

Tôi đã thử một số cách tiếp cận, tất cả đều không thành công. Đây là những gì tôi đã thử:

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
}

Tệp nhật ký được tạo, nhưng không có gì được in hoặc thêm vào nó. Tại sao?


2
Nếu bạn triển khai chương trình của mình trong Linux, bạn chỉ có thể ghi nhật ký của mình vào đầu ra std sau đó chuyển đầu ra vào một tệp như: ./program 2> & 1 | tee logs.txt . Phải có một số cách khác trong hệ thống khác.
nvcnvn 14/11/13

Câu trả lời:


165

os.Open() chắc hẳn đã hoạt động khác trong quá khứ, nhưng điều này phù hợp với tôi:

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

Dựa trên tài liệu Go, os.Open()không thể hoạt động log.SetOutputvì nó mở tệp "để đọc:"

func Open

func Open(name string) (file *File, err error) Openmở tệp đã đặt tên để đọc. Nếu thành công, các phương thức trên tệp trả về có thể được sử dụng để đọc; bộ mô tả tệp được liên kết có chế độ O_RDONLY. Nếu có lỗi sẽ bị loại *PathError.

BIÊN TẬP

Chuyển defer f.Close()đến sau khi if err != nilkiểm tra


9
Đừng trì hoãn Đóng trước khi kiểm tra lỗi cho con số không!
Volker

Nó không thực sự có hại khi đóng cửa trong mọi trường hợp. Tuy nhiên, điều đó không đúng với tất cả các loại.
Dustin

2
@Dustin fcó thể là nil, điều này sẽ dẫn đến một sự hoảng loạn. Vì vậy, kiểm tra errtrước khi hoãn cuộc gọi là điều nên làm.
nemo

@AllisonAn muốn giải thích tại sao Openkhông hoạt động với log.SetOutput?
nemo

1
Các quyền an toàn hơn là 0644 hoặc thậm chí 0664 để cho phép người dùng đọc / ghi, người dùng và nhóm đọc / ghi và trong cả hai trường hợp đều không cho phép mọi người viết.
Jonathan

38

Tôi thích sự đơn giản và linh hoạt của đề xuất ứng dụng 12 yếu tố để ghi nhật ký. Để thêm vào tệp nhật ký, bạn có thể sử dụng chuyển hướng trình bao. Trình ghi mặc định trong Go ghi vào stderr (2).

./app 2>> logfile

Xem thêm: http://12factor.net/logs


wont có một thói quen tốt khi bạn muốn daemonize mọi thứ, đặc biệt là với khởi TSOP-daemon
Shrey

3
@Shrey Systemd có thể dễ dàng thực hiện việc ghi nhật ký, cũng như về các chức năng dừng bắt đầu.
WarGasm

Mặc dù đây có phải là một thực hành tốt hay không, đây là kiểu ghi nhật ký mà tôi đang tìm kiếm ở Golang. Cảm ơn vì đã chia sẻ điều này!
nghiện

Có cái gì đó tương tự dưới cửa sổ?
lướt sóng vào

Giống như $ 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' tôi KHÔNG làm việcUbuntu 18.04.3
Ryosuke Hujisawa

20

Tôi thường in nhật ký trên màn hình và viết thành tệp. Hy vọng điều này sẽ giúp ai đó.

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

Điều này phù hợp với tôi

  1. đã tạo một gói có tên 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. nhập gói bất cứ nơi nào bạn muốn đăng nhập, ví dụ: 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

Nếu bạn chạy nhị phân trên máy linux, bạn có thể sử dụng tập lệnh shell.

ghi đè lên một tệp

./binaryapp > binaryapp.log

nối vào một tệp

./binaryapp >> binaryapp.log

ghi đè stderr vào một tệp

./binaryapp &> binaryapp.error.log

nối stderr vào một tệp

./binaryapp &>> binalyapp.error.log

nó có thể năng động hơn bằng cách sử dụng tệp kịch bản shell.


Rất vui được biết, làm thế nào để chúng tôi ghi đè stderr để đăng nhập.
không thể,

5

Trình ghi mặc định trong Go ghi vào stderr (2). chuyển hướng đến tệp

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

Khai báo hàng đầu trong toàn cầu của bạn varđể tất cả các quy trình của bạn có thể truy cập nếu cần.

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

Xin chào @CostaHuang, vui lòng để lại phản hồi chi tiết. Cảm ơn
openwonk

@CostaHuang, tôi vừa chạy đoạn mã của mình và nó hoạt động.
openwonk

Xin chào @openwonk, tôi đã kiểm tra lại và nó không hoạt động trên máy tính của tôi. Phiên bản của tôi là go version go1.10.2 windows/amd64, phiên bản của bạn là gì?
Costa Huang,

@CostaHuang, tôi vừa chạy ví dụ với thiết lập tương tự như bạn. Ví dụ giả định rằng bạn đã thiết lập cấu trúc thư mục. Có nhiều cách dễ dàng để kiểm tra điều này, tuy nhiên, mục tiêu của tôi với ví dụ là cho thấy cách ghi tương đối đơn giản vào tệp nhật ký. Thay đổi mã của bạn thành outfile, _ = os.Create("my.log")và nó sẽ hoạt động như mong đợi.
openwonk

Mã của bạn hoạt động. Tôi đã sử dụng outfile, _ = os.Create("./path/to/my.log"). Bằng cách nào đó, tôi đã mong đợi rằng mã sẽ tạo các path/tothư mục và my.logtệp, nhưng rõ ràng là nó không hoạt động. Tôi khuyên bạn nên sửa đổi câu trả lời của bạn để được outfile, _ = os.Create("./my.log"). Bằng cách đó, chúng tôi biết rõ ràng rằng nó đang tạo nhật ký trong thư mục hiện tại.
Costa Huang

5

Dựa trên câu trả lời của Allison và Deepak, tôi bắt đầu sử dụng logrus và thực sự thích nó:

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

Tôi có một defer f.Close () trong hàm chính


0

Tôi đang ghi nhật ký vào các tệp, được tạo hàng ngày (mỗi ngày, một tệp nhật ký được tạo). Cách tiếp cận này đang hoạt động tốt đối với tôi:

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
}

Phương thức "shortFilePath ()" được sử dụng để lấy tên của tệp từ đường dẫn đầy đủ của tệp. và phương thức "LogServer ()" được sử dụng để tạo một câu lệnh nhật ký được định dạng (chứa: tên tệp, số dòng, cấp độ nhật ký, câu lệnh lỗi, v.v.)


0

Để giúp những người khác, tôi tạo một hàm nhật ký cơ bản để xử lý việc ghi nhật ký trong cả hai trường hợp, nếu bạn muốn đầu ra là ổn định, thì hãy bật gỡ lỗi, ngay lập tức nó sẽ thực hiện một cờ chuyển đổi để bạn có thể chọn đầu ra của mình.

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

có thể điều này sẽ giúp bạn (nếu tệp nhật ký tồn tại, hãy sử dụng nó, nếu nó không tồn tại, hãy tạo nó):

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

Để biết thêm chi tiết: https://su9.co/9BAE74B

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.