github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/error-reporting/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"os/exec"
    10  	"syscall"
    11  )
    12  
    13  type TailWriter struct {
    14  	Head         int // writing head
    15  	TotalWritten int
    16  	Buffer       []byte
    17  }
    18  
    19  func NewTail(size int) *TailWriter {
    20  	return &TailWriter{0, 0, make([]byte, size, size)}
    21  }
    22  
    23  func (w *TailWriter) Write(data []byte) (int, error) {
    24  	w.TotalWritten += len(data)
    25  
    26  	if len(data) > len(w.Buffer) {
    27  		copy(w.Buffer, data[len(data)-len(w.Buffer):])
    28  		w.Head = 0
    29  	} else {
    30  		n := copy(w.Buffer[w.Head:], data)
    31  		if w.Head >= len(w.Buffer) {
    32  			w.Head = 0
    33  		}
    34  
    35  		n = copy(w.Buffer[:], data[n:])
    36  		w.Head += n
    37  	}
    38  
    39  	return len(data), nil
    40  }
    41  
    42  func (tw *TailWriter) WriteTo(w io.Writer) {
    43  	if tw.TotalWritten <= len(tw.Buffer) {
    44  		w.Write(tw.Buffer[0:tw.TotalWritten])
    45  		return
    46  	}
    47  	w.Write(tw.Buffer[tw.Head:])
    48  	w.Write(tw.Buffer[:tw.Head])
    49  }
    50  
    51  func monitor() {
    52  	if os.Getenv("__NOMONITOR__") != "" {
    53  		return
    54  	}
    55  
    56  	cmd := exec.Command(os.Args[0], os.Args...)
    57  	cmd.Env = []string{"__NOMONITOR__=true"}
    58  	cmd.Env = append(cmd.Env, os.Environ()...)
    59  
    60  	tailout := NewTail(2 << 10)
    61  	tailerr := NewTail(2 << 10)
    62  
    63  	cmd.Stdin = os.Stdin
    64  	cmd.Stdout = io.MultiWriter(os.Stdout, tailout)
    65  	cmd.Stderr = io.MultiWriter(os.Stderr, tailerr)
    66  
    67  	// TODO: also propagate signals
    68  
    69  	err := cmd.Run()
    70  	if err != nil {
    71  		// send email here
    72  		var data bytes.Buffer
    73  		data.WriteString("=== STDOUT ===\n")
    74  		tailout.WriteTo(&data)
    75  		data.WriteString("\n\n=== STDERR ===\n")
    76  		tailerr.WriteTo(&data)
    77  		data.WriteString("\n\n")
    78  
    79  		ioutil.WriteFile("error.txt", data.Bytes(), 0755)
    80  	}
    81  
    82  	// propagate exit-code
    83  	if exiterr, ok := err.(*exec.ExitError); ok {
    84  		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
    85  			os.Exit(status.ExitStatus())
    86  		}
    87  	}
    88  
    89  	os.Exit(0)
    90  }
    91  
    92  func main() {
    93  	monitor()
    94  
    95  	fmt.Println("hello")
    96  	panic("we broke stuff")
    97  }