2016-01-13 24 views
11

में पैनिक कैप्चरिंग() हमारे पास एक बड़े-आश गोलांग एप्लिकेशन है जो लॉगर (वास्तव में, एक कस्टम लॉगर) का उपयोग करता है, जो समय-समय पर घुमाए गए लॉग फ़ाइल में आउटपुट लिखने के लिए होता है।गोलांग

हालांकि, जब कोई एप्लिकेशन क्रैश या पैनिक() है, तो वे संदेश मानक त्रुटि पर जाते हैं।

क्या हमारे लॉगर का उपयोग करने के लिए आतंक कार्यक्षमता को ओवरराइड करने का कोई तरीका है?

+0

मुझे लगता है कि 'पुनर्प्राप्ति' आउटपुट को रीडायरेक्ट करने से बेहतर है। 'debug.PrintStack' आपको आवश्यकतानुसार लॉग प्रिंट कर सकता है। कुछ अन्य लॉग, उदाहरण के लिए, जीसी संदेश, अभी भी stderr में रह सकते हैं। – Bryce

उत्तर

9

जहाँ तक मुझे पता है, आप मानक त्रुटि से या अपने लॉगर से पैनिक से आउटपुट को रीडायरेक्ट नहीं कर सकते हैं। सबसे अच्छी बात यह है कि आप मानक त्रुटि को उस फ़ाइल में रीडायरेक्ट कर सकते हैं जिसे आप बाहरी रूप से या अपने प्रोग्राम के अंदर कर सकते हैं।

मेरे rclone प्रोग्राम के लिए मैंने एक विकल्प पर फ़ाइल में सबकुछ कैप्चर करने के लिए मानक त्रुटि को पुनर्निर्देशित किया है, दुर्भाग्य से क्रॉस प्लेटफ़ॉर्म तरीके से करना विशेष रूप से आसान नहीं है। यहाँ कैसे मैंने किया

लिनक्स के लिए (पुन: निर्देशन * .go फ़ाइलें देखें)/यूनिक्स

// Log the panic under unix to the log file 

//+build unix 

package main 

import (
    "log" 
    "os" 
    "syscall" 
) 

// redirectStderr to the file passed in 
func redirectStderr(f *os.File) { 
    err := syscall.Dup2(int(f.Fd()), int(os.Stderr.Fd())) 
    if err != nil { 
     log.Fatalf("Failed to redirect stderr to file: %v", err) 
    } 
} 

और खिड़कियों

// Log the panic under windows to the log file 
// 
// Code from minix, via 
// 
// http://play.golang.org/p/kLtct7lSUg 

//+build windows 

package main 

import (
    "log" 
    "os" 
    "syscall" 
) 

var (
    kernel32   = syscall.MustLoadDLL("kernel32.dll") 
    procSetStdHandle = kernel32.MustFindProc("SetStdHandle") 
) 

func setStdHandle(stdhandle int32, handle syscall.Handle) error { 
    r0, _, e1 := syscall.Syscall(procSetStdHandle.Addr(), 2, uintptr(stdhandle), uintptr(handle), 0) 
    if r0 == 0 { 
     if e1 != 0 { 
      return error(e1) 
     } 
     return syscall.EINVAL 
    } 
    return nil 
} 

// redirectStderr to the file passed in 
func redirectStderr(f *os.File) { 
    err := setStdHandle(syscall.STD_ERROR_HANDLE, syscall.Handle(f.Fd())) 
    if err != nil { 
     log.Fatalf("Failed to redirect stderr to file: %v", err) 
    } 
    // SetStdHandle does not affect prior references to stderr 
    os.Stderr = f 
} 
+0

यह मेरे लिए काम करता है - मेरे मुद्दे को हल करने के बजाय पैनिक्स मेरे एफडी पर जा रहा है। –

+0

मैंने कोशिश की और यूनिक्स पक्ष ने मेरे लिए काम किया लेकिन विंडोज़ पक्ष नहीं ... कोई अंतर्दृष्टि? - https://play.golang.org/p/jQaN1bNypx –

+0

मैंने कुछ और प्रयोग किया और विंडो पर आपको 'os.Stderr = f' भी करना चाहिए क्योंकि 'SetStdHandle' stderr के पूर्व संदर्भ को प्रभावित नहीं करता है। यूनिक्स पर यह आवश्यक नहीं है क्योंकि 'Dup2' stderr के पूर्व संदर्भ को प्रभावित करता है। https://play.golang.org/p/mXjw6lOrG9 –

5

आप उसी goroutine से पैनिक्स पुनर्प्राप्त करने के लिए recover() का उपयोग कर सकते हैं। एक स्थगित विधि में recover() पर कॉल करते समय (याद रखें कि स्थगित विधियों को अभी भी कहा जाएगा, भले ही panic() आईएनजी), यह अंतिम panic() कॉल को तर्क के रूप में कॉल करेगा (या nil जब प्रोग्राम घबराहट नहीं हो रहा है)।

defer func() { 
    if x := recover(); x != nil { 
     // recovering from a panic; x contains whatever was passed to panic() 
     log.Printf("run time panic: %v", x) 

     // if you just want to log the panic, panic again 
     panic(x) 
    } 
}() 

panic("foo"); 

नोट तथापि, कि तुम घबरा है कि एक अलग goroutine में उन्हें ट्रिगर किया गया से वापस नहीं ले पाता (धन्यवाद संकेत के लिए JimB के लिए)। किसी भी goroutine से पैनिक्स से पुनर्प्राप्त करने के लिए एक recover() का उपयोग करना संभव नहीं है।

+3

मुझे लगता है कि आप केवल अपने ही goroutine के भीतर से ठीक हो सकते हैं। मुझे लगता है कि ओपी वैश्विक "पुनर्प्राप्ति" की तलाश में हो सकता है, जिसे आप नहीं कर सकते। – JimB

+0

सच है, वास्तव में। मैं इसे संपादित करूँगा, संकेत के लिए धन्यवाद। – helmbert

+0

खैर, मुझे आतंक को रोकने की जरूरत नहीं है, मैं बस अपने लॉग में बैकट्रैक दिखाना चाहता हूं, न कि stderr। मुझे लगता है कि मैं अपने लॉगजर के साथ os.Stderr को प्रतिस्थापित कर सकता हूं? नहीं - ओएस के साथ भी चेक किया गया है, मेरे लॉगफाइल हैंडल होने के कारण, आतंक अभी भी stderr पर जाता है। –

0

विस्तार @ निक-क्रेग लकड़ी के जवाब के लिए: यदि आप लिनक्स पर हैं तो आप लॉगर (1) इंस्टेंस को स्पैन कर सकते हैं और इसके लिए stderr को रीडायरेक्ट कर सकते हैं। इस तरह आप syslog में पूर्ण बैकट्रैक प्राप्त करते हैं। यह gocryptfs करता है:

// redirectStdFds redirects stderr and stdout to syslog; stdin to /dev/null 
func redirectStdFds() { 
    // stderr and stdout 
    pr, pw, err := os.Pipe() 
    if err != nil { 
     tlog.Warn.Printf("redirectStdFds: could not create pipe: %v\n", err) 
     return 
    } 
    tag := fmt.Sprintf("gocryptfs-%d-logger", os.Getpid()) 
    cmd := exec.Command("logger", "-t", tag) 
    cmd.Stdout = os.Stdout 
    cmd.Stderr = os.Stderr 
    cmd.Stdin = pr 
    err = cmd.Start() 
    if err != nil { 
     tlog.Warn.Printf("redirectStdFds: could not start logger: %v\n", err) 
    } 
    pr.Close() 
    err = syscall.Dup2(int(pw.Fd()), 1) 
    if err != nil { 
     tlog.Warn.Printf("redirectStdFds: stdout dup error: %v\n", err) 
    } 
    syscall.Dup2(int(pw.Fd()), 2) 
    if err != nil { 
     tlog.Warn.Printf("redirectStdFds: stderr dup error: %v\n", err) 
    } 
    pw.Close() 

    // stdin 
    nullFd, err := os.Open("/dev/null") 
    if err != nil { 
     tlog.Warn.Printf("redirectStdFds: could not open /dev/null: %v\n", err) 
     return 
    } 
    err = syscall.Dup2(int(nullFd.Fd()), 0) 
    if err != nil { 
     tlog.Warn.Printf("redirectStdFds: stdin dup error: %v\n", err) 
    } 
    nullFd.Close() 
}