Golang: 로그 캡처 및 이메일 보내기

17460 단어 logsgoemail
우리 회사에는 파트너와의 통합을 처리하기 위해 Golang으로 작성된 ETL이 있습니다. 각 통합은 cronjob k8s를 사용하여 고유하고 격리된 POD에서 실행되며, 각각은 log 표준 라이브러리의 패키지에서 이러한 모든 로그는 다른 도구와의 통합을 모니터링하는 데 유용합니다.

우리 팀에서는 이제 프로세스의 로그와 일부 통합에 실패했을 때 이메일을 받기를 원하므로 log 기능을 사용하여 SetOutput 라는 표준 로거의 출력 대상을 변경합니다.
io.MultiWriter를 사용하여 여러 라이터를 결합한 라이터를 만들 수 있습니다. 이 경우에는 버퍼와 표준os.Stderr을 결합하여 로그를 버퍼에 저장하고 모니터링을 위해 로그를 유지합니다stderr.

패키지log는 기본값으로 os.Stderr를 사용합니다.

        buf := new(bytes.Buffer)
        w := io.MultiWriter(buf, os.Stderr)
        log.SetOutput(w)


그런 다음 모든 로그가 버퍼에 있으므로 임시 파일에 저장하고 이메일에 첨부할 파일을 만들 수 있습니다.

    f, err := os.Create("/path/log.txt")
    if err != nil {
        // Handler ....
    }

    defer f.Close()

    // We copy the buffer into the file.
    if _, err := io.Copy(f, buf); err != nil {
        // Handler ....
    }


이제 로그 파일이 포함된 이메일을 보낼 수 있습니다. 표준 라이브러리만 사용하여 첨부 파일이 포함된 이메일을 보내는 몇 가지 구현을 찾았지만 작동하지 않아 다른 접근 방식을 결합하여 이 구현을 얻었습니다.

package main

import (
    "bytes"
    "encoding/base64"
    "fmt"
    "io/ioutil"
    "mime/multipart"
    "net/smtp"
    "os"
    "path/filepath"
)

var (
    host       = os.Getenv("EMAIL_HOST")
    username   = os.Getenv("EMAiL_USERNAME")
    password   = os.Getenv("EMAIL_PASSWORD")
    portNumber = os.Getenv("EMAIL_PORT")
)

type Sender struct {
    auth smtp.Auth
}

type Message struct {
    To          []string
    Subject     string
    Body        string
    Attachments map[string][]byte
}

func New() *Sender {
    auth := smtp.PlainAuth("", username, password, host)
    return &Sender{auth}, nil
}

func (s *Sender) Send(m *Message) error {
    return smtp.SendMail(fmt.Sprintf("%s:%s", host, portNumber), s.auth, username, m.To, m.ToBytes())
}

func NewMessage(s, b string) *Message {
    return &Message{Subject: s, Body: b, Attachments: make(map[string][]byte)}
}

func (m *Message) AttachFile(src string) error {
    b, err := ioutil.ReadFile(src)
    if err != nil {
        return err
    }

    _, fileName := filepath.Split(src)
    m.Attachments[fileName] = b
    return nil
}

func (m *Message) ToBytes() []byte {
    buf := bytes.NewBuffer(nil)
    withAttachments := len(m.Attachments) > 0

    buf.WriteString(fmt.Sprintf("Subject: %s\n", m.Subject))
    buf.WriteString("MIME-Version: 1.0\n")
    writer := multipart.NewWriter(buf)
    boundary := writer.Boundary()

    if withAttachments {
        buf.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\n", boundary))
        buf.WriteString(fmt.Sprintf("--%s\n", boundary))
    }

    buf.WriteString("Content-Type: text/plain; charset=utf-8\n")
    buf.WriteString(m.Body)

    if withAttachments {
        for k, v := range m.Attachments {
            buf.WriteString(fmt.Sprintf("\n\n--%s\n", boundary))
            buf.WriteString("Content-Type: application/octet-stream\n")
            buf.WriteString("Content-Transfer-Encoding: base64\n")
            buf.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=%s\n", k))

            b := make([]byte, base64.StdEncoding.EncodedLen(len(v)))
            base64.StdEncoding.Encode(b, v)
            buf.Write(b)
            buf.WriteString(fmt.Sprintf("\n--%s", boundary))
        }

        buf.WriteString("--")
    }

    return buf.Bytes()
}

func main() {
    sender := New()
    m := NewMessage("Test", "Body message.")
    m.To = []string{"[email protected]"}
    m.AttachFile("/path/to/file")
    fmt.Println(s.Send(m))
}


이 구현을 개선하기 위한 몇 가지 팁이나 더 잘할 수 있는 한 가지 방법이 있다면 놀라운 일이 될 것입니다.

Github .

좋은 웹페이지 즐겨찾기