You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

160 lines
3.0 KiB

// Copyright 2017 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package clog
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
)
type slackAttachment struct {
Text string `json:"text"`
Color string `json:"color"`
}
type slackPayload struct {
Attachments []slackAttachment `json:"attachments"`
}
var slackColors = []string{
"", // Trace
"#3aa3e3", // Info
"warning", // Warn
"danger", // Error
"#ff0200", // Fatal
}
type SlackConfig struct {
// Minimum level of messages to be processed.
Level LEVEL
// Buffer size defines how many messages can be queued before hangs.
BufferSize int64
// Slack webhook URL.
URL string
}
type slack struct {
Adapter
url string
}
func newSlack() Logger {
return &slack{
Adapter: Adapter{
quitChan: make(chan struct{}),
},
}
}
func (s *slack) Level() LEVEL { return s.level }
func (s *slack) Init(v interface{}) error {
cfg, ok := v.(SlackConfig)
if !ok {
return ErrConfigObject{"SlackConfig", v}
}
if !isValidLevel(cfg.Level) {
return ErrInvalidLevel{}
}
s.level = cfg.Level
if len(cfg.URL) == 0 {
return errors.New("URL cannot be empty")
}
s.url = cfg.URL
s.msgChan = make(chan *Message, cfg.BufferSize)
return nil
}
func (s *slack) ExchangeChans(errorChan chan<- error) chan *Message {
s.errorChan = errorChan
return s.msgChan
}
func buildSlackPayload(msg *Message) (string, error) {
payload := slackPayload{
Attachments: []slackAttachment{
{
Text: msg.Body,
Color: slackColors[msg.Level],
},
},
}
p, err := json.Marshal(&payload)
if err != nil {
return "", err
}
return string(p), nil
}
func (s *slack) write(msg *Message) {
payload, err := buildSlackPayload(msg)
if err != nil {
s.errorChan <- fmt.Errorf("slack: buildSlackPayload: %v", err)
return
}
resp, err := http.Post(s.url, "application/json", bytes.NewReader([]byte(payload)))
if err != nil {
s.errorChan <- fmt.Errorf("slack: %v", err)
return
}
defer resp.Body.Close()
if resp.StatusCode/100 != 2 {
data, _ := ioutil.ReadAll(resp.Body)
s.errorChan <- fmt.Errorf("slack: %s", data)
}
}
func (s *slack) Start() {
LOOP:
for {
select {
case msg := <-s.msgChan:
s.write(msg)
case <-s.quitChan:
break LOOP
}
}
for {
if len(s.msgChan) == 0 {
break
}
s.write(<-s.msgChan)
}
s.quitChan <- struct{}{} // Notify the cleanup is done.
}
func (s *slack) Destroy() {
s.quitChan <- struct{}{}
<-s.quitChan
close(s.msgChan)
close(s.quitChan)
}
func init() {
Register(SLACK, newSlack)
}