mirror of
https://github.com/prometheus/alertmanager
synced 2025-02-21 13:17:01 +00:00
Filter by labels on alerts
This should be encoded in the URL and stored there, and used as the "source of truth" for parsing and propagating the filters.
This commit is contained in:
parent
4d4791e0fb
commit
9126426eff
@ -1,19 +1,24 @@
|
||||
module Alerts.Translator exposing (translator)
|
||||
|
||||
import Alerts.Types exposing (Msg(..), Alert, AlertsMsg, OutMsg(..))
|
||||
import Utils.Types exposing (Filter)
|
||||
|
||||
|
||||
type alias TranslationDictionary msg =
|
||||
{ onInternalMessage : AlertsMsg -> msg
|
||||
, onSilenceFromAlert : Alert -> msg
|
||||
, onUpdateFilter : Filter -> String -> msg
|
||||
}
|
||||
|
||||
|
||||
translator : TranslationDictionary parentMsg -> Msg -> parentMsg
|
||||
translator { onInternalMessage, onSilenceFromAlert } msg =
|
||||
translator { onInternalMessage, onSilenceFromAlert, onUpdateFilter } msg =
|
||||
case msg of
|
||||
ForSelf internal ->
|
||||
onInternalMessage internal
|
||||
|
||||
ForParent (SilenceFromAlert alert) ->
|
||||
onSilenceFromAlert alert
|
||||
|
||||
ForParent (UpdateFilter filter string) ->
|
||||
onUpdateFilter filter string
|
||||
|
@ -2,7 +2,7 @@ module Alerts.Types exposing (..)
|
||||
|
||||
import Http exposing (Error)
|
||||
import ISO8601
|
||||
import Utils.Types exposing (ApiData)
|
||||
import Utils.Types exposing (ApiData, Filter)
|
||||
|
||||
|
||||
type Route
|
||||
@ -16,12 +16,14 @@ type Msg
|
||||
|
||||
type OutMsg
|
||||
= SilenceFromAlert Alert
|
||||
| UpdateFilter Filter String
|
||||
|
||||
|
||||
type AlertsMsg
|
||||
= AlertGroupsFetch (ApiData (List AlertGroup))
|
||||
| FetchAlertGroups
|
||||
| Noop
|
||||
| FilterAlerts
|
||||
|
||||
|
||||
type alias Block =
|
||||
|
@ -3,22 +3,154 @@ module Alerts.Update exposing (..)
|
||||
import Alerts.Api as Api
|
||||
import Alerts.Types exposing (..)
|
||||
import Task
|
||||
import Utils.Types exposing (ApiData, ApiResponse(..))
|
||||
import Utils.Types exposing (ApiData, ApiResponse(..), Filter)
|
||||
import Regex
|
||||
|
||||
|
||||
update : AlertsMsg -> ApiData (List AlertGroup) -> ( ApiData (List AlertGroup), Cmd Msg )
|
||||
update msg groups =
|
||||
update : AlertsMsg -> ApiData (List AlertGroup) -> Filter -> ( ApiData (List AlertGroup), Filter, Cmd Msg )
|
||||
update msg groups filter =
|
||||
case msg of
|
||||
AlertGroupsFetch alertGroups ->
|
||||
( alertGroups, Cmd.none )
|
||||
( alertGroups, filter, Cmd.none )
|
||||
|
||||
FetchAlertGroups ->
|
||||
( groups, Api.getAlertGroups )
|
||||
( groups, filter, Api.getAlertGroups )
|
||||
|
||||
Noop ->
|
||||
( groups, Cmd.none )
|
||||
( groups, filter, Cmd.none )
|
||||
|
||||
FilterAlerts ->
|
||||
let
|
||||
f =
|
||||
case groups of
|
||||
Success groups ->
|
||||
let
|
||||
replace =
|
||||
(Regex.replace Regex.All (Regex.regex "{|}|\"|\\s") (\_ -> ""))
|
||||
|
||||
matches =
|
||||
String.split "," (replace filter.text)
|
||||
|
||||
labels =
|
||||
List.filterMap
|
||||
(\m ->
|
||||
let
|
||||
label =
|
||||
String.split "=" m
|
||||
in
|
||||
if List.length label == 2 then
|
||||
Just ( Maybe.withDefault "" (List.head label), Maybe.withDefault "" (List.head <| List.reverse label) )
|
||||
else
|
||||
Nothing
|
||||
)
|
||||
matches
|
||||
in
|
||||
-- Instead of changing filter, change the query string and that then is parsed into the filter structure
|
||||
{ filter | labels = labels }
|
||||
|
||||
_ ->
|
||||
filter
|
||||
in
|
||||
( groups, f, Cmd.none )
|
||||
|
||||
|
||||
urlUpdate : Route -> AlertsMsg
|
||||
urlUpdate _ =
|
||||
FetchAlertGroups
|
||||
|
||||
|
||||
filterBy : (a -> Maybe a) -> List a -> List a
|
||||
filterBy fn groups =
|
||||
List.filterMap fn groups
|
||||
|
||||
|
||||
filterByReceiver : Maybe String -> List AlertGroup -> List AlertGroup
|
||||
filterByReceiver maybeReceiver groups =
|
||||
case maybeReceiver of
|
||||
Just receiver ->
|
||||
filterBy (filterAlertGroup receiver) groups
|
||||
|
||||
Nothing ->
|
||||
groups
|
||||
|
||||
|
||||
filterAlertGroup : String -> AlertGroup -> Maybe AlertGroup
|
||||
filterAlertGroup receiver alertGroup =
|
||||
let
|
||||
blocks =
|
||||
List.filter (\b -> receiver == b.routeOpts.receiver) alertGroup.blocks
|
||||
in
|
||||
if not <| List.isEmpty blocks then
|
||||
Just { alertGroup | blocks = blocks }
|
||||
else
|
||||
Nothing
|
||||
|
||||
|
||||
filterBySilenced : Maybe Bool -> List AlertGroup -> List AlertGroup
|
||||
filterBySilenced maybeShowSilenced groups =
|
||||
case maybeShowSilenced of
|
||||
Just showSilenced ->
|
||||
groups
|
||||
|
||||
Nothing ->
|
||||
filterBy filterAlertGroupSilenced groups
|
||||
|
||||
|
||||
filterAlertsFromBlock : (Alert -> Bool) -> Block -> Maybe Block
|
||||
filterAlertsFromBlock fn block =
|
||||
let
|
||||
alerts =
|
||||
List.filter fn block.alerts
|
||||
in
|
||||
if not <| List.isEmpty alerts then
|
||||
Just { block | alerts = alerts }
|
||||
else
|
||||
Nothing
|
||||
|
||||
|
||||
filterAlertsByLabel : List ( String, String ) -> Block -> Maybe Block
|
||||
filterAlertsByLabel labels block =
|
||||
filterAlertsFromBlock
|
||||
(\a ->
|
||||
-- Check that all labels are present within the alert's label set.
|
||||
List.all
|
||||
(\l ->
|
||||
List.member l a.labels
|
||||
)
|
||||
labels
|
||||
)
|
||||
block
|
||||
|
||||
|
||||
filterAlertGroupLabels : List ( String, String ) -> AlertGroup -> Maybe AlertGroup
|
||||
filterAlertGroupLabels labels alertGroup =
|
||||
let
|
||||
blocks =
|
||||
List.filterMap (filterAlertsByLabel labels) alertGroup.blocks
|
||||
in
|
||||
if not <| List.isEmpty blocks then
|
||||
Just { alertGroup | blocks = blocks }
|
||||
else
|
||||
Nothing
|
||||
|
||||
|
||||
filterAlertGroupSilenced : AlertGroup -> Maybe AlertGroup
|
||||
filterAlertGroupSilenced alertGroup =
|
||||
let
|
||||
blocks =
|
||||
List.filterMap filterSilencedAlerts alertGroup.blocks
|
||||
in
|
||||
if not <| List.isEmpty blocks then
|
||||
Just { alertGroup | blocks = blocks }
|
||||
else
|
||||
Nothing
|
||||
|
||||
|
||||
filterSilencedAlerts : Block -> Maybe Block
|
||||
filterSilencedAlerts block =
|
||||
filterAlertsFromBlock (\a -> not a.silenced) block
|
||||
|
||||
|
||||
filterByLabels : List ( String, String ) -> List AlertGroup -> List AlertGroup
|
||||
filterByLabels labels groups =
|
||||
filterBy (filterAlertGroupLabels labels) groups
|
||||
|
@ -2,12 +2,41 @@ module Alerts.Views exposing (view)
|
||||
|
||||
import Alerts.Types exposing (Alert, AlertGroup, Block, Route(..))
|
||||
import Alerts.Types exposing (AlertsMsg(..), Msg(..), OutMsg(..))
|
||||
import Alerts.Update exposing (filterBySilenced, filterByReceiver, filterByLabels)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick)
|
||||
import Utils.Date
|
||||
import Utils.Types exposing (Filter)
|
||||
import Utils.Views exposing (..)
|
||||
|
||||
|
||||
view : Route -> List AlertGroup -> Filter -> Html Msg
|
||||
view route alertGroups filter =
|
||||
let
|
||||
groups =
|
||||
case route of
|
||||
Receiver maybeReceiver maybeShowSilenced ->
|
||||
filterByReceiver maybeReceiver alertGroups
|
||||
|> filterBySilenced maybeShowSilenced
|
||||
|> filterByLabels filter.labels
|
||||
in
|
||||
if List.isEmpty groups then
|
||||
div [] [ text "no alerts found found" ]
|
||||
else
|
||||
div []
|
||||
[ Html.map ForParent (textField "Filter" filter.text (UpdateFilter filter))
|
||||
, a [ class "f6 link br2 ba ph3 pv2 mr2 dib blue", onClick (ForSelf FilterAlerts) ] [ text "Filter Alerts" ]
|
||||
, ul
|
||||
[ classList
|
||||
[ ( "list", True )
|
||||
, ( "pa0", True )
|
||||
]
|
||||
]
|
||||
(List.map alertGroupView groups)
|
||||
]
|
||||
|
||||
|
||||
alertGroupView : AlertGroup -> Html Msg
|
||||
alertGroupView alertGroup =
|
||||
li [ class "pa3 pa4-ns bb b--black-10" ]
|
||||
@ -54,84 +83,3 @@ alertHeader ( key, value ) =
|
||||
b [ class "db f4 mr2 dark-red dib" ] [ text value ]
|
||||
else
|
||||
listButton "ph1 pv1" ( key, value )
|
||||
|
||||
|
||||
view : Route -> List AlertGroup -> Html Msg
|
||||
view route alertGroups =
|
||||
let
|
||||
groups =
|
||||
case route of
|
||||
Receiver maybeReceiver maybeShowSilenced ->
|
||||
filterBySilenced maybeShowSilenced <| filterByReceiver maybeReceiver alertGroups
|
||||
in
|
||||
if List.isEmpty groups then
|
||||
div [] [ text "no alerts found found" ]
|
||||
else
|
||||
ul
|
||||
[ classList
|
||||
[ ( "list", True )
|
||||
, ( "pa0", True )
|
||||
]
|
||||
]
|
||||
(List.map alertGroupView groups)
|
||||
|
||||
|
||||
filterBy : (a -> Maybe a) -> List a -> List a
|
||||
filterBy fn groups =
|
||||
List.filterMap fn groups
|
||||
|
||||
|
||||
filterByReceiver : Maybe String -> List AlertGroup -> List AlertGroup
|
||||
filterByReceiver maybeReceiver groups =
|
||||
case maybeReceiver of
|
||||
Just receiver ->
|
||||
filterBy (filterAlertGroup receiver) groups
|
||||
|
||||
Nothing ->
|
||||
groups
|
||||
|
||||
|
||||
filterAlertGroup : String -> AlertGroup -> Maybe AlertGroup
|
||||
filterAlertGroup receiver alertGroup =
|
||||
let
|
||||
blocks =
|
||||
List.filter (\b -> receiver == b.routeOpts.receiver) alertGroup.blocks
|
||||
in
|
||||
if not <| List.isEmpty blocks then
|
||||
Just { alertGroup | blocks = blocks }
|
||||
else
|
||||
Nothing
|
||||
|
||||
|
||||
filterBySilenced : Maybe Bool -> List AlertGroup -> List AlertGroup
|
||||
filterBySilenced maybeShowSilenced groups =
|
||||
case maybeShowSilenced of
|
||||
Just showSilenced ->
|
||||
groups
|
||||
|
||||
Nothing ->
|
||||
filterBy filterAlertGroupSilenced groups
|
||||
|
||||
|
||||
filterAlertGroupSilenced : AlertGroup -> Maybe AlertGroup
|
||||
filterAlertGroupSilenced alertGroup =
|
||||
let
|
||||
blocks =
|
||||
List.filterMap filterSilencedAlerts alertGroup.blocks
|
||||
in
|
||||
if not <| List.isEmpty blocks then
|
||||
Just { alertGroup | blocks = blocks }
|
||||
else
|
||||
Nothing
|
||||
|
||||
|
||||
filterSilencedAlerts : Block -> Maybe Block
|
||||
filterSilencedAlerts block =
|
||||
let
|
||||
alerts =
|
||||
List.filter (\a -> not a.silenced) block.alerts
|
||||
in
|
||||
if not <| List.isEmpty alerts then
|
||||
Just { block | alerts = alerts }
|
||||
else
|
||||
Nothing
|
||||
|
18
src/Main.elm
18
src/Main.elm
@ -30,7 +30,8 @@ init location =
|
||||
route =
|
||||
Parsing.urlParser location
|
||||
in
|
||||
update (urlUpdate location) (Model Loading Loading Loading route)
|
||||
-- Need to parse out the filter text with the url parser
|
||||
update (urlUpdate location) (Model Loading Loading Loading route ({ text = "", labels = [] }))
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
@ -55,17 +56,17 @@ update msg model =
|
||||
alertsMsg =
|
||||
(Alerts.Update.urlUpdate alertsRoute)
|
||||
|
||||
( alertGroups, alertCmd ) =
|
||||
Alerts.Update.update alertsMsg model.alertGroups
|
||||
( alertGroups, filter, alertCmd ) =
|
||||
Alerts.Update.update alertsMsg model.alertGroups model.filter
|
||||
in
|
||||
( { model | alertGroups = alertGroups, route = AlertsRoute alertsRoute }, Cmd.map alertTranslator alertCmd )
|
||||
( { model | alertGroups = alertGroups, filter = filter, route = AlertsRoute alertsRoute }, Cmd.map alertTranslator alertCmd )
|
||||
|
||||
Alerts alertsMsg ->
|
||||
let
|
||||
( alertGroups, alertCmd ) =
|
||||
Alerts.Update.update alertsMsg model.alertGroups
|
||||
( alertGroups, filter, alertCmd ) =
|
||||
Alerts.Update.update alertsMsg model.alertGroups model.filter
|
||||
in
|
||||
( { model | alertGroups = alertGroups }, Cmd.map alertTranslator alertCmd )
|
||||
( { model | alertGroups = alertGroups, filter = filter }, Cmd.map alertTranslator alertCmd )
|
||||
|
||||
Silences silencesMsg ->
|
||||
let
|
||||
@ -92,6 +93,9 @@ update msg model =
|
||||
RedirectAlerts ->
|
||||
( model, Navigation.newUrl "/#/alerts" )
|
||||
|
||||
UpdateFilter filter text ->
|
||||
( { model | filter = { filter | text = text } }, Cmd.none )
|
||||
|
||||
Noop ->
|
||||
( model, Cmd.none )
|
||||
|
||||
|
@ -4,7 +4,7 @@ import Alerts.Types
|
||||
import Alerts.Translator
|
||||
import Silences.Types
|
||||
import Silences.Translator
|
||||
import Types exposing (Msg(Alerts, CreateSilenceFromAlert, Silences))
|
||||
import Types exposing (Msg(Alerts, CreateSilenceFromAlert, Silences, UpdateFilter))
|
||||
|
||||
|
||||
alertTranslator : Alerts.Types.Msg -> Msg
|
||||
@ -12,6 +12,7 @@ alertTranslator =
|
||||
Alerts.Translator.translator
|
||||
{ onInternalMessage = Alerts
|
||||
, onSilenceFromAlert = CreateSilenceFromAlert
|
||||
, onUpdateFilter = UpdateFilter
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,7 +7,7 @@ import Silences.Types exposing (SilencesMsg, Silence)
|
||||
import Http exposing (Error)
|
||||
import ISO8601
|
||||
import Time
|
||||
import Utils.Types exposing (ApiData)
|
||||
import Utils.Types exposing (ApiData, Filter)
|
||||
|
||||
|
||||
-- Internal Imports
|
||||
@ -19,6 +19,7 @@ type alias Model =
|
||||
, silence : ApiData Silence
|
||||
, alertGroups : ApiData (List AlertGroup)
|
||||
, route : Route
|
||||
, filter : Filter
|
||||
}
|
||||
|
||||
|
||||
@ -30,6 +31,7 @@ type Msg
|
||||
| NewSilence
|
||||
| EditSilence Int
|
||||
| CreateSilenceFromAlert Alert
|
||||
| UpdateFilter Filter String
|
||||
| NavigateToAlerts Alerts.Types.Route
|
||||
| Alerts AlertsMsg
|
||||
| Silences SilencesMsg
|
||||
|
@ -10,6 +10,12 @@ type ApiResponse e a
|
||||
| Success a
|
||||
|
||||
|
||||
type alias Filter =
|
||||
{ text : String
|
||||
, labels : List ( String, String )
|
||||
}
|
||||
|
||||
|
||||
type alias ApiData a =
|
||||
ApiResponse Http.Error a
|
||||
|
||||
|
@ -22,7 +22,7 @@ view model =
|
||||
AlertsRoute route ->
|
||||
case model.alertGroups of
|
||||
Success alertGroups ->
|
||||
Html.map alertTranslator (Alerts.Views.view route alertGroups)
|
||||
Html.map alertTranslator (Alerts.Views.view route alertGroups model.filter)
|
||||
|
||||
Loading ->
|
||||
loading
|
||||
|
Loading…
Reference in New Issue
Block a user