29 Jan 2012

SMTP Logger in GO

Go is really intuitive to write and one thing I like most is the interface design.

So I wrote a SMTP log handler that has a Write method, meaning it implements io.Writer, and can be used anywhere that requires io.Writer, e.g. log.new:

func New(out io.Writer, prefix string, flag int) *Logger

So that I can use:

logger := log.New(sw, "SMTPLOG: ", 0)
logger.Print("This is a test sent from SMTP Logger\n\nnemo")

sw is a SMTP log handler:

type SMTPWriter struct {
    auth                   smtp.Auth
    user, pass, host, port string
    sendto                 []string
    subject                string
}

func (w *SMTPWriter) Write(b []byte) (n int, err error) {
    header := fmt.Sprintf(`Subject: %s
From: %s
To: %s
Content-Type: text/plain; charset=ISO-8859-1

`, w.subject, w.user, strings.Join(w.sendto, ";"))
    err = smtp.SendMail(
        w.host+":"+w.port,
        w.auth,
        w.user,
        w.sendto,
        append([]byte(header), b...),
    )
    if err != nil {
        return 0, err
    }
    return len(b), nil
}

func NewSMTPWriter(user, pass, host, port string,
    sendto []string, subject string) *SMTPWriter {
    var auth = smtp.PlainAuth("", user, pass, host)
    return &SMTPWriter{auth, user, pass, host, port, sendto, subject}
}
sw := NewSMTPWriter(
    "test@jlee.co",    // user
    "test@jlee.co",    // pass
    "smtp.gmail.com",  // server
    "587",             // port
    []string{"me@jlee.co", "jackie.space@gmai.com"},  // sendto
    "Testing smtpwriter in a logger",                 // subject
)
And then like what you expect, you can use:

logger.Print("This is a test sent from SMTP Logger\n\nnemo")

But it'll send a email on every call on logger's method. That's too verbose. I want to send a email when there is say 1024 chars.
Go has a bufio package. We can wrap our SMTPWriter using bufio:

func NewBufSMTPWriter(user, pass, host, port string,
    sendto []string, subject string, bufsize int) (*bufio.Writer, error) {
    sw := NewSMTPWriter(user, pass, host, port, sendto, subject)
    w, err := bufio.NewWriterSize(sw, bufsize)
    if err != nil {
         return nil, err
    }
    return w, nil
}


See? you don't have to define a new BufSMTPWriter, just return any struct that has a Write method, here I have a *buifio.Writer return type, you can even use io.Writer return type, because the target method call only cares if you have a Write method.

the code is at https://github.com/jackielii/smtpwriter

No comments: