แนวทางที่ถูกต้องในการบันทึกทั่วโลกใน Golang


120

รูปแบบสำหรับการเข้าสู่ระบบแอปพลิเคชัน Go คืออะไร? ถ้าฉันมีบอกว่า 5 goroutines ที่ฉันต้องออกจากระบบฉันควรจะ ...

  • สร้างซิงเกิ้ลlog.Loggerและส่งต่อ?
  • ผ่านตัวชี้ไปที่log.Logger?
  • แต่ละ goroutine หรือฟังก์ชันควรสร้างคนตัดไม้หรือไม่?
  • ฉันควรสร้างคนตัดไม้เป็นตัวแปรส่วนกลางหรือไม่

คำตอบ:


59
  • สร้างบันทึกเดียวเข้าสู่ระบบและส่งต่อ?

ที่เป็นไปได้ log.Loggerสามารถนำมาใช้ควบคู่กันจากหลาย goroutines

  • ส่งตัวชี้ไปยังบันทึกนั้น Logger?

log.Newส่งคืน*Loggerซึ่งโดยปกติจะเป็นตัวบ่งชี้ว่าคุณควรส่งวัตถุไปรอบ ๆ เป็นตัวชี้ การส่งผ่านเป็นค่าจะเป็นการสร้างสำเนาของโครงสร้าง (เช่นสำเนาของ Logger) จากนั้น goroutines หลายตัวอาจเขียนไปยังio Writerเดียวกันพร้อมกัน นั่นอาจเป็นปัญหาร้ายแรงขึ้นอยู่กับการดำเนินการของนักเขียน

  • แต่ละ goroutine หรือฟังก์ชันควรสร้างคนตัดไม้หรือไม่?

ฉันจะไม่สร้างคนตัดไม้แยกต่างหากสำหรับแต่ละฟังก์ชันหรือ goroutine Goroutines (และฟังก์ชัน) ใช้สำหรับงานที่มีน้ำหนักเบามากซึ่งจะไม่เป็นเหตุให้ต้องดูแลรักษาเครื่องตัดไม้แยกต่างหาก อาจเป็นความคิดที่ดีที่จะสร้างคนตัดไม้สำหรับแต่ละองค์ประกอบที่ใหญ่กว่าในโครงการของคุณ ตัวอย่างเช่นหากโปรเจ็กต์ของคุณใช้บริการ SMTP ในการส่งอีเมลการสร้างตัวบันทึกแยกต่างหากสำหรับบริการอีเมลดูเหมือนเป็นความคิดที่ดีเพื่อให้คุณสามารถกรองและปิดเอาต์พุตแยกกันได้

  • ฉันควรสร้างคนตัดไม้เป็นตัวแปรส่วนกลางหรือไม่

ขึ้นอยู่กับแพ็คเกจของคุณ ในตัวอย่างบริการอีเมลก่อนหน้านี้อาจเป็นความคิดที่ดีที่จะมีตัวบันทึกหนึ่งตัวสำหรับแต่ละอินสแตนซ์ของบริการของคุณเพื่อให้ผู้ใช้สามารถบันทึกความล้มเหลวในขณะที่ใช้บริการเมลของ Gmail ได้แตกต่างจากความล้มเหลวที่เกิดขึ้นขณะใช้ MTA ในเครื่อง (เช่น sendmail )


37

log.Loggerสำหรับกรณีที่เรียบง่ายมีการตัดไม้ทั่วโลกที่กำหนดไว้ในแพคเกจล็อก log.SetFlagsนี้ตัดไม้ทั่วโลกสามารถกำหนดค่าผ่าน

หลังจากนั้นก็สามารถเรียกใช้ฟังก์ชันระดับบนสุดของแพ็กเกจบันทึกเช่นlog.Printfและlog.Fatalfซึ่งใช้อินสแตนซ์ส่วนกลางนั้น


คิดว่าคุณสามารถตั้งค่าสถานะที่คุณไม่สามารถใช้คนตัดไม้แบบกำหนดเองได้
0xcaff

@caffinatedmonkey จริง ๆ แล้วคุณสามารถใช้คนตัดไม้ที่กำหนดเองได้หากพวกเขาใช้io.Writerอินเทอร์เฟซและคุณเปลี่ยนผลลัพธ์ของตัวบันทึกเริ่มต้นผ่านSetOutput().
Congusbongus

16

นี่คือคนตัดไม้ง่ายๆ

package customlogger

import (
    "log"
    "os"
    "sync"
)

type logger struct {
    filename string
    *log.Logger
}

var logger *logger
var once sync.Once

// start loggeando
func GetInstance() *logger {
    once.Do(func() {
        logger = createLogger("mylogger.log")
    })
    return logger
}

func createLogger(fname string) *logger {
    file, _ := os.OpenFile(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)

    return &logger{
        filename: fname,
        Logger:   log.New(file, "My app Name ", log.Lshortfile),
    }
}

คุณสามารถใช้วิธีนี้ได้

package main

import (
    "customlogger"
    "fmt"
    "net/http"
)

func main() {
    logger := customlogger.GetInstance()
    logger.Println("Starting")

    http.HandleFunc("/", sroot)
    http.ListenAndServe(":8080", nil)
}

func sroot(w http.ResponseWriter, r *http.Request) {
    logger := customlogger.GetInstance()

    fmt.Fprintf(w, "welcome")
    logger.Println("Starting")
}

10

ฉันรู้ว่าคำถามนี้ค่อนข้างเก่า แต่ถ้าเช่นฉันโครงการของคุณประกอบด้วยไฟล์ขนาดเล็กหลายไฟล์ฉันโหวตให้ตัวเลือกที่ 4 ของคุณ - ฉันได้สร้างlogger.goที่เป็นส่วนหนึ่งของแพ็กเกจหลักแล้ว ไฟล์ go นี้สร้างตัวบันทึกกำหนดให้กับไฟล์และจัดเตรียมให้กับไฟล์หลักที่เหลือ หมายเหตุฉันไม่ได้คิดวิธีปิด errorlog ที่สวยงาม ...

package main

import (
    "fmt"
    "log"
    "os"
)

var errorlog *os.File
var logger *log.Logger

func init() {
    errorlog, err := os.OpenFile(logfile,  os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        fmt.Printf("error opening file: %v", err)
        os.Exit(1)
    }

    logger = log.New(errorlog, "applog: ", log.Lshortfile|log.LstdFlags)
}

8
สำหรับการปิดที่สวยงามคุณอาจdefer errorlog.Close()เมื่อสิ้นสุดการดำเนินการหรือเพื่อให้แน่ใจว่าปิดแล้วให้ตั้งค่าตัวจัดการสัญญาณโดยใช้แพ็คเกจสัญญาณของ Go golang.org/pkg/os/signal
Anfernee

4

นี่เป็นคำถามที่เก่ากว่า แต่ฉันอยากแนะนำให้ใช้http://github.com/romana/rlog (ที่เราพัฒนาขึ้น) มีการกำหนดค่าผ่านตัวแปรสภาพแวดล้อมอ็อบเจ็กต์ logger ถูกสร้างและเตรียมใช้งานเมื่อนำเข้า rlog ดังนั้นไม่จำเป็นต้องผ่านคนตัดไม้

rlog มีคุณสมบัติค่อนข้างน้อย:

  • แสตมป์วันที่ / เวลาที่กำหนดได้อย่างสมบูรณ์
  • เอาต์พุตพร้อมกันไปยัง stderr หรือ stdout เช่นเดียวกับไฟล์
  • ระดับการบันทึกมาตรฐาน (ดีบักข้อมูล ฯลฯ ) ตลอดจนการบันทึกหลายระดับที่กำหนดค่าได้อย่างอิสระ
  • การบันทึกข้อมูลผู้โทรตามความต้องการ (ไฟล์หมายเลขบรรทัดฟังก์ชัน)
  • ความสามารถในการตั้งค่าระดับการบันทึกที่แตกต่างกันสำหรับไฟล์ต้นฉบับที่แตกต่างกัน

มีขนาดเล็กมากไม่มีการอ้างอิงภายนอกยกเว้นไลบรารี Golang มาตรฐานและกำลังได้รับการพัฒนาอย่างต่อเนื่อง ตัวอย่างมีให้ใน repo


3
ขอขอบคุณสำหรับการเปิดเผยความเกี่ยวข้องของคุณกับผลิตภัณฑ์ที่คุณแนะนำ! เป็นที่ชื่นชม
Robert Columbia

2

ฉันพบว่าแพ็คเกจบันทึกเริ่มต้น ( https://golang.org/pkg/log/ ) ค่อนข้าง จำกัด ตัวอย่างเช่นไม่รองรับข้อมูลเทียบกับบันทึกการแก้ไขข้อบกพร่อง
หลังจากเล่นไปเรื่อยๆ ก็เริ่มใช้https://github.com/golang/glog ดูเหมือนว่าจะเป็นพอร์ตของhttps://github.com/google/glogและให้ความยืดหยุ่นในการบันทึก ตัวอย่างเช่นเมื่อเรียกใช้แอปพลิเคชันในเครื่องคุณอาจต้องการบันทึกระดับ DEBUG แต่อาจต้องการเรียกใช้เฉพาะในระดับ INFO / ERROR ในการใช้งานจริง รายการคุณสมบัติ / คู่มือฉบับเต็มอยู่ที่นี่https://google-glog.googlecode.com/svn/trunk/doc/glog.html (สำหรับโมดูล c ++ แต่ส่วนใหญ่แปลเป็นพอร์ตโกแลง)


0

หนึ่งในโมดูลการเข้าสู่ระบบที่คุณสามารถพิจารณาเป็นklog รองรับการบันทึก 'V' ซึ่งให้ความยืดหยุ่นในการบันทึกในระดับหนึ่ง

klog เป็นทางแยกของ glog และเอาชนะข้อบกพร่องที่ตามมา

  • glog นำเสนอ "gotchas" จำนวนมากและแนะนำความท้าทายในสภาพแวดล้อมที่มีคอนเทนเนอร์ซึ่งทั้งหมดนี้ไม่ได้รับการบันทึกไว้เป็นอย่างดี
  • glog ไม่มีวิธีง่ายๆในการทดสอบบันทึกซึ่งจะลดความเสถียรของซอฟต์แวร์ที่ใช้งาน
  • glog ใช้ C ++ และ klog เป็นการใช้งาน golang ที่บริสุทธิ์

การใช้งานตัวอย่าง

package main

import (
    "flag"

    "k8s.io/klog"


)

type myError struct {
    str string
}

func (e myError) Error() string {
    return e.str
}

func main() {
    klog.InitFlags(nil)
    flag.Set("v", "1")
    flag.Parse()

    klog.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
    klog.V(3).Info("nice to meet you")
    klog.Error(nil, "uh oh", "trouble", true, "reasons", []float64{0.1, 0.11, 3.14})
    klog.Error(myError{"an error occurred"}, "goodbye", "code", -1)
    klog.Flush()
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.