Watcher - Actions conditions in foreach

Hello,
I am trying to create a watcher that must send a different slack message depending on the field "state" from each log in the hits.hits The slack message must have:
"color" = "good" if the state is finished
"color" = "warning" if the state is started
"color" = "error" if the state is failed.

Basically, for every hit that the watcher receives from the query, I want to send a slack message with a specific color based on the state field.

A very simplified input of what the watcher might be receiving:

{
    "hits": {
        "hits": [
          {
            "_index": "com1:log-1-4X-7",
            "_type": "_doc",
            "_source": {
              "level": 30,
              "description": "Log description here",
              "state": "finished",
              "clientId": "1",
              "eventName" : "RENTING"
            },
            "_id": "7-x",
            "_score": 49.16513
          },
          {
            "_index": "com1:log-1-4X-7",
            "_type": "_doc",
            "_source": {
              "level": 30,
              "description": "Another Log description here",
              "state": "started",
              "clientId": "2",
              "eventName" : "BUYING"
            },
            "_id": "A-x",
            "_score": 48.56805
          }      
        ],
        "total": 2,
        "max_score": 49.16513
      }
    }

How could I set up these multiple actions for the conditions mentioned above?
I've tried to create a watcher like the one bellow, but I receive this error below in the simulate tab:

    "actions": [
      {
        "id": "notify-slack2",
        "type": "slack",
        "status": "condition_failed",
        "reason": "condition failed. skipping: runtime error"
      },
      {
        "id": "notify-slack",
        "type": "slack",
        "status": "condition_failed",
        "reason": "condition failed. skipping: runtime error"
      }
    ]
  }

The watcher that I am using to test this:

{
  "trigger": {
    "schedule": {
      "interval": "30m"
    }
  },
  "input": {
    "search": {
      "request": {
        "body": {
          "size": 0,
          "query": {
            "match_all": {}
          }
        },
        "indices": [
          "*"
        ]
      }
    }
  },
  "condition": {
    "compare": {
      "ctx.payload.hits.total": {
        "gte": 0
      }
    }
  },
   "actions": {
        "notify-slack": {
            "foreach": "ctx.payload.hits.hits",
            "condition": {
                "script": {
                    "source": " return ctx.payload._source.state == 'started'",
                    "lang": "painless"
                }
            },
            "slack": {
                "message": {
                    "to": [
                        "#test-channel"
                    ],
                    "attachments": [
                        {
                            "color": "warning",
                            "title": "It is starting",
                            "text": "{{ctx.payload._source.state}}"
                        }
                    ]
                }
            }
        },
        "notify-slack2": {
            "foreach": "ctx.payload.hits.hits",
            "condition": {
                "script": {
                    "source": " return ctx.payload._source.state == 'finished'",
                    "lang": "painless"
                }
            },
            "slack": {
                "message": {
                    "to": [
                        "#test-channel"
                    ],
                    "attachments": [
                        {
                            "color": "good",
                            "title": "It is finisehd",
                            "text": "{{ctx.payload._source.state}}"
                        }
                    ]
                }
            }
        }
    }
}

I am not sure if it is an issue accessing ctx.payload._source.state in the script because it is under a foreach.

I tested setting the script condition to 1==1 and the message came with the text state right, so {{ctx.payload._source.state}} works fine inside the action.
How could I set up this script condition for each action as it must run foreach hit? Elastic version: 7.17.7

I did a simple mockup using your two example documents.

I used a transform block to build a data structure (and ArrayList, for example) of all of the things you want to alert on. For example:

    "transform": {
      "script": """
            def my_hits = new ArrayList();
            for (def my_hit : ctx.payload.hits.hits) {
              if (my_hit._source.state == "started") {
                  def info = new HashMap();
                  info.put("color","warning");
                  info.put("state","started");
                  info.put("title","It is starting");
                  my_hits.add(info);
                }
                else if (my_hit._source.state == "finished") {
                  def info = new HashMap();
                  info.put("color","good");
                  info.put("state","finished");
                  info.put("title","It is finished");
                  my_hits.add(info);
                }
                else if (my_hit._source.state == "failed") {
                  def info = new HashMap();
                  info.put("color","error");
                  info.put("state","failed");
                  info.put("title","It is failed");
                  my_hits.add(info);
                }
            }
      return my_hits;
      """
    },
    "actions": {
      "log": {
        "foreach": "ctx.payload._value",
        "logging": {
          "text": """
		        {{ctx.payload}}
		      """
        }
      }
    }

The output data structure after the transform block looks like:

        "payload": {
          "_value": [
            {
              "color": "good",
              "state": "finished",
              "title": "It is finished"
            },
            {
              "color": "warning",
              "state": "started",
              "title": "It is starting"
            }
          ]
        }
      },

Obviously I chose to log rather than slack, but you get the idea

1 Like

@v_watch I'd rather prefer using aggregations instead of querying the data and then transforming.

  1. Use terms aggregation on event -> key will be the event name
  2. Use terms aggregation on state -> key will give state of the event
  3. Condition can simply check if event buckets length > 0
  4. A single slack action with {{#ctx.payload.aggregations.event.buckets}}{{key}} has state {{state.buckets[0].key}}{{/ctx.payload.aggregations.event.buckets}}

Also, you can refer to conditional actions on watcher here: Adding conditions to Watcher actions | Elasticsearch Guide [master] | Elastic

@Ayush_Mathur that's true except there's still the missing requirement of mapping state to color

Thank you soo much Rich!!!!

Using the transform approach as you did worked perfectly and now I can customize the slack message further to make it even better if I need.
This made the watcher much better than trying to set up a bunch of actions with conditions.

I really appreacite your help, thank you again!

1 Like

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.