mirror of
https://github.com/grafana/grafana.git
synced 2025-02-09 23:16:16 -06:00
feat(alerting): working on alert notification and image rendering
This commit is contained in:
parent
f9ddfb4337
commit
2b276d5cd1
@ -3,7 +3,6 @@ package imguploader
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@ -11,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
type Uploader interface {
|
||||
Upload(imgUrl string) (string, error)
|
||||
Upload(path string) (string, error)
|
||||
}
|
||||
|
||||
type S3Uploader struct {
|
||||
@ -28,13 +27,7 @@ func NewS3Uploader(bucket, accessKey, secretKey string) *S3Uploader {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *S3Uploader) Upload(imgUrl string) (string, error) {
|
||||
client := http.Client{Timeout: time.Duration(60 * time.Second)}
|
||||
|
||||
res, err := client.Get(imgUrl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
func (u *S3Uploader) Upload(path string) (string, error) {
|
||||
|
||||
s3util.DefaultConfig.AccessKey = u.accessKey
|
||||
s3util.DefaultConfig.SecretKey = u.secretKey
|
||||
@ -53,7 +46,7 @@ func (u *S3Uploader) Upload(imgUrl string) (string, error) {
|
||||
|
||||
defer writer.Close()
|
||||
|
||||
imgData, err := ioutil.ReadAll(res.Body)
|
||||
imgData, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -9,10 +9,11 @@ import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type RenderOpts struct {
|
||||
@ -23,8 +24,10 @@ type RenderOpts struct {
|
||||
Timeout string
|
||||
}
|
||||
|
||||
var rendererLog log.Logger = log.New("png-renderer")
|
||||
|
||||
func RenderToPng(params *RenderOpts) (string, error) {
|
||||
log.Info("PhantomRenderer::renderToPng url %v", params.Url)
|
||||
rendererLog.Info("Rendering", "url", params.Url)
|
||||
|
||||
var executable = "phantomjs"
|
||||
if runtime.GOOS == "windows" {
|
||||
@ -71,11 +74,12 @@ func RenderToPng(params *RenderOpts) (string, error) {
|
||||
select {
|
||||
case <-time.After(time.Duration(timeout) * time.Second):
|
||||
if err := cmd.Process.Kill(); err != nil {
|
||||
log.Error(4, "failed to kill: %v", err)
|
||||
rendererLog.Error("failed to kill", "error", err)
|
||||
}
|
||||
return "", fmt.Errorf("PhantomRenderer::renderToPng timeout (>%vs)", timeout)
|
||||
case <-done:
|
||||
}
|
||||
|
||||
rendererLog.Debug("Image rendered", "path", pngPath)
|
||||
return pngPath, nil
|
||||
}
|
||||
|
@ -1,25 +1,31 @@
|
||||
package alerting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type EvalContext struct {
|
||||
Firing bool
|
||||
IsTestRun bool
|
||||
Events []*Event
|
||||
Logs []*ResultLogEntry
|
||||
Error error
|
||||
Description string
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
Rule *Rule
|
||||
DoneChan chan bool
|
||||
CancelChan chan bool
|
||||
log log.Logger
|
||||
Firing bool
|
||||
IsTestRun bool
|
||||
Events []*Event
|
||||
Logs []*ResultLogEntry
|
||||
Error error
|
||||
Description string
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
Rule *Rule
|
||||
DoneChan chan bool
|
||||
CancelChan chan bool
|
||||
log log.Logger
|
||||
dashboardSlug string
|
||||
ImagePublicUrl string
|
||||
ImageOnDiskPath string
|
||||
}
|
||||
|
||||
func (a *EvalContext) GetDurationMs() float64 {
|
||||
@ -50,6 +56,38 @@ func (c *EvalContext) GetStateText() string {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *EvalContext) getDashboardSlug() (string, error) {
|
||||
if c.dashboardSlug != "" {
|
||||
return c.dashboardSlug, nil
|
||||
}
|
||||
|
||||
slugQuery := &m.GetDashboardSlugByIdQuery{Id: c.Rule.DashboardId}
|
||||
if err := bus.Dispatch(slugQuery); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
c.dashboardSlug = slugQuery.Result
|
||||
return c.dashboardSlug, nil
|
||||
}
|
||||
|
||||
func (c *EvalContext) GetRuleUrl() (string, error) {
|
||||
if slug, err := c.getDashboardSlug(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
ruleUrl := fmt.Sprintf("%sdashboard/db/%s?fullscreen&edit&tab=alert&panelId=%d", setting.AppUrl, slug, c.Rule.PanelId)
|
||||
return ruleUrl, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *EvalContext) GetImageUrl() (string, error) {
|
||||
if slug, err := c.getDashboardSlug(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
ruleUrl := fmt.Sprintf("%sdashboard-solo/db/%s?&panelId=%d", setting.AppUrl, slug, c.Rule.PanelId)
|
||||
return ruleUrl, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewEvalContext(rule *Rule) *EvalContext {
|
||||
return &EvalContext{
|
||||
StartTime: time.Now(),
|
||||
|
@ -4,8 +4,11 @@ import (
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/imguploader"
|
||||
"github.com/grafana/grafana/pkg/components/renderer"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type RootNotifier struct {
|
||||
@ -35,12 +38,51 @@ func (n *RootNotifier) Notify(context *EvalContext) {
|
||||
return
|
||||
}
|
||||
|
||||
err = n.uploadImage(context)
|
||||
if err != nil {
|
||||
n.log.Error("Failed to upload alert panel image", "error", err)
|
||||
}
|
||||
|
||||
for _, notifier := range notifiers {
|
||||
n.log.Info("Sending notification", "firing", context.Firing, "type", notifier.GetType())
|
||||
go notifier.Notify(context)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *RootNotifier) uploadImage(context *EvalContext) error {
|
||||
uploader := imguploader.NewS3Uploader(
|
||||
setting.S3TempImageStoreBucketUrl,
|
||||
setting.S3TempImageStoreAccessKey,
|
||||
setting.S3TempImageStoreSecretKey)
|
||||
|
||||
imageUrl, err := context.GetImageUrl()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
renderOpts := &renderer.RenderOpts{
|
||||
Url: imageUrl,
|
||||
Width: "800",
|
||||
Height: "400",
|
||||
SessionId: "123",
|
||||
Timeout: "10",
|
||||
}
|
||||
|
||||
if imagePath, err := renderer.RenderToPng(renderOpts); err != nil {
|
||||
return err
|
||||
} else {
|
||||
context.ImageOnDiskPath = imagePath
|
||||
}
|
||||
|
||||
context.ImagePublicUrl, err = uploader.Upload(context.ImageOnDiskPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.log.Info("uploaded", "url", context.ImagePublicUrl)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *RootNotifier) getNotifiers(orgId int64, notificationIds []int64) ([]Notifier, error) {
|
||||
query := &m.GetAlertNotificationsQuery{OrgId: orgId, Ids: notificationIds}
|
||||
|
||||
|
@ -1,20 +1 @@
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func getRuleLink(rule *alerting.Rule) (string, error) {
|
||||
slugQuery := &m.GetDashboardSlugByIdQuery{Id: rule.DashboardId}
|
||||
if err := bus.Dispatch(slugQuery); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ruleLink := fmt.Sprintf("%sdashboard/db/%s?fullscreen&edit&tab=alert&panelId=%d", setting.AppUrl, slugQuery.Result, rule.PanelId)
|
||||
return ruleLink, nil
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ func NewEmailNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
|
||||
func (this *EmailNotifier) Notify(context *alerting.EvalContext) {
|
||||
this.log.Info("Sending alert notification to", "addresses", this.Addresses)
|
||||
|
||||
ruleLink, err := getRuleLink(context.Rule)
|
||||
ruleUrl, err := context.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
return
|
||||
@ -50,7 +50,7 @@ func (this *EmailNotifier) Notify(context *alerting.EvalContext) {
|
||||
"RuleState": context.Rule.State,
|
||||
"RuleName": context.Rule.Name,
|
||||
"Severity": context.Rule.Severity,
|
||||
"RuleLink": ruleLink,
|
||||
"RuleUrl": ruleUrl,
|
||||
},
|
||||
To: this.Addresses,
|
||||
Template: "alert_notification.html",
|
||||
|
@ -41,7 +41,7 @@ func (this *SlackNotifier) Notify(context *alerting.EvalContext) {
|
||||
|
||||
rule := context.Rule
|
||||
|
||||
ruleLink, err := getRuleLink(rule)
|
||||
ruleUrl, err := context.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
return
|
||||
@ -69,10 +69,10 @@ func (this *SlackNotifier) Notify(context *alerting.EvalContext) {
|
||||
// "author_link": "http://flickr.com/bobby/",
|
||||
// "author_icon": "http://flickr.com/icons/bobby.jpg",
|
||||
"title": "[" + context.GetStateText() + "] " + rule.Name,
|
||||
"title_link": ruleLink,
|
||||
"title_link": ruleUrl,
|
||||
// "text": "Optional text that appears within the attachment",
|
||||
"fields": fields,
|
||||
// "image_url": "http://my-website.com/path/to/image.jpg",
|
||||
"fields": fields,
|
||||
"image_url": context.ImagePublicUrl,
|
||||
// "thumb_url": "http://example.com/path/to/thumb.png",
|
||||
"footer": "Grafana v4.0.0",
|
||||
"footer_icon": "http://grafana.org/assets/img/fav32.png",
|
||||
|
@ -148,6 +148,11 @@ var (
|
||||
|
||||
// Grafana.NET URL
|
||||
GrafanaNetUrl string
|
||||
|
||||
// S3 temp image store
|
||||
S3TempImageStoreBucketUrl string
|
||||
S3TempImageStoreAccessKey string
|
||||
S3TempImageStoreSecretKey string
|
||||
)
|
||||
|
||||
type CommandLineArgs struct {
|
||||
@ -534,6 +539,10 @@ func NewConfigContext(args *CommandLineArgs) error {
|
||||
|
||||
GrafanaNetUrl = Cfg.Section("grafana.net").Key("url").MustString("https://grafana.net")
|
||||
|
||||
s3temp := Cfg.Section("s3-temp-image-store")
|
||||
S3TempImageStoreBucketUrl = s3temp.Key("bucket_url").String()
|
||||
S3TempImageStoreAccessKey = s3temp.Key("access_key").String()
|
||||
S3TempImageStoreSecretKey = s3temp.Key("secret_key").String()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
11
vendor/phantomjs/render.js
vendored
11
vendor/phantomjs/render.js
vendored
@ -35,6 +35,17 @@
|
||||
page.open(params.url, function (status) {
|
||||
// console.log('Loading a web page: ' + params.url + ' status: ' + status);
|
||||
|
||||
page.onError = function(msg, trace) {
|
||||
var msgStack = ['ERROR: ' + msg];
|
||||
if (trace && trace.length) {
|
||||
msgStack.push('TRACE:');
|
||||
trace.forEach(function(t) {
|
||||
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function +'")' : ''));
|
||||
});
|
||||
}
|
||||
console.error(msgStack.join('\n'));
|
||||
};
|
||||
|
||||
function checkIsReady() {
|
||||
var panelsRendered = page.evaluate(function() {
|
||||
if (!window.angular) { return false; }
|
||||
|
Loading…
Reference in New Issue
Block a user