Example #1
0
export const getAPMIndexPattern = memoize(async () => {
  const apmIndexPatternTitle: string = chrome.getInjected(
    'apmIndexPatternTitle'
  );
  const res = await callApi<ISavedObjectAPIResponse>({
    pathname: `/api/saved_objects/_find`,
    query: {
      type: 'index-pattern',
      search: `"${apmIndexPatternTitle}"`,
      search_fields: 'title',
      per_page: 200
    }
  });

  return res.saved_objects.find(
    savedObject => savedObject.attributes.title === apmIndexPatternTitle
  );
});
export async function createErrorGroupWatch({
  emails = [],
  schedule,
  serviceName,
  slackUrl,
  threshold,
  timeRange
}: Arguments) {
  const id = `apm-${uuid.v4()}`;
  const apmIndexPatternTitle = chrome.getInjected('apmIndexPatternTitle');

  const slackUrlPath = getSlackPathUrl(slackUrl);
  const emailTemplate = i18n.translate(
    'xpack.apm.serviceDetails.enableErrorReportsPanel.emailTemplateText',
    {
      defaultMessage:
        'Your service {serviceName} has error groups which exceeds {threshold} occurrences within {timeRange}{br}' +
        '{br}' +
        '{errorGroupsBuckets}{br}' +
        '{errorLogMessage}{br}' +
        '{errorCulprit}N/A{slashErrorCulprit}{br}' +
        '{docCountParam} occurrences{br}' +
        '{slashErrorGroupsBucket}',
      values: {
        serviceName: '"{{ctx.metadata.serviceName}}"',
        threshold: '{{ctx.metadata.threshold}}',
        timeRange:
          '"{{ctx.metadata.timeRangeValue}}{{ctx.metadata.timeRangeUnit}}"',
        errorGroupsBuckets:
          '{{#ctx.payload.aggregations.error_groups.buckets}}',
        errorLogMessage:
          '<strong>{{sample.hits.hits.0._source.error.log.message}}{{^sample.hits.hits.0._source.error.log.message}}{{sample.hits.hits.0._source.error.exception.0.message}}{{/sample.hits.hits.0._source.error.log.message}}</strong>',
        errorCulprit:
          '{{sample.hits.hits.0._source.error.culprit}}{{^sample.hits.hits.0._source.error.culprit}}',
        slashErrorCulprit: '{{/sample.hits.hits.0._source.error.culprit}}',
        docCountParam: '{{doc_count}}',
        slashErrorGroupsBucket:
          '{{/ctx.payload.aggregations.error_groups.buckets}}',
        br: '<br/>'
      }
    }
  );

  const slackTemplate = i18n.translate(
    'xpack.apm.serviceDetails.enableErrorReportsPanel.slackTemplateText',
    {
      defaultMessage: `Your service {serviceName} has error groups which exceeds {threshold} occurrences within {timeRange}
{errorGroupsBuckets}
{errorLogMessage}
{errorCulprit}N/A{slashErrorCulprit}
{docCountParam} occurrences
{slashErrorGroupsBucket}`,
      values: {
        serviceName: '"{{ctx.metadata.serviceName}}"',
        threshold: '{{ctx.metadata.threshold}}',
        timeRange:
          '"{{ctx.metadata.timeRangeValue}}{{ctx.metadata.timeRangeUnit}}"',
        errorGroupsBuckets:
          '{{#ctx.payload.aggregations.error_groups.buckets}}',
        errorLogMessage:
          '>*{{sample.hits.hits.0._source.error.log.message}}{{^sample.hits.hits.0._source.error.log.message}}{{sample.hits.hits.0._source.error.exception.0.message}}{{/sample.hits.hits.0._source.error.log.message}}*',
        errorCulprit:
          '>{{#sample.hits.hits.0._source.error.culprit}}`{{sample.hits.hits.0._source.error.culprit}}`{{/sample.hits.hits.0._source.error.culprit}}{{^sample.hits.hits.0._source.error.culprit}}',
        slashErrorCulprit: '{{/sample.hits.hits.0._source.error.culprit}}',
        docCountParam: '>{{doc_count}}',
        slashErrorGroupsBucket:
          '{{/ctx.payload.aggregations.error_groups.buckets}}'
      }
    }
  );

  const actions: Actions = {
    log_error: { logging: { text: emailTemplate } }
  };

  const body = {
    metadata: {
      emails,
      trigger: i18n.translate(
        'xpack.apm.serviceDetails.enableErrorReportsPanel.triggerText',
        {
          defaultMessage: 'This value must be changed in trigger section'
        }
      ),
      serviceName,
      threshold,
      timeRangeValue: timeRange.value,
      timeRangeUnit: timeRange.unit,
      slackUrlPath
    },
    trigger: {
      schedule
    },
    input: {
      search: {
        request: {
          indices: [apmIndexPatternTitle],
          body: {
            size: 0,
            query: {
              bool: {
                filter: [
                  { term: { [SERVICE_NAME]: '{{ctx.metadata.serviceName}}' } },
                  { term: { [PROCESSOR_EVENT]: 'error' } },
                  {
                    range: {
                      '@timestamp': {
                        gte:
                          'now-{{ctx.metadata.timeRangeValue}}{{ctx.metadata.timeRangeUnit}}'
                      }
                    }
                  }
                ]
              }
            },
            aggs: {
              error_groups: {
                terms: {
                  min_doc_count: '{{ctx.metadata.threshold}}',
                  field: ERROR_GROUP_ID,
                  size: 10,
                  order: {
                    _count: 'desc'
                  }
                },
                aggs: {
                  sample: {
                    top_hits: {
                      _source: [
                        ERROR_LOG_MESSAGE,
                        ERROR_EXC_MESSAGE,
                        ERROR_EXC_HANDLED,
                        ERROR_CULPRIT,
                        ERROR_GROUP_ID,
                        '@timestamp'
                      ],
                      sort: [
                        {
                          '@timestamp': 'desc'
                        }
                      ],
                      size: 1
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    condition: {
      script: {
        source:
          'return ctx.payload.aggregations.error_groups.buckets.length > 0'
      }
    },
    actions
  };

  if (slackUrlPath) {
    body.actions.slack_webhook = {
      webhook: {
        scheme: 'https',
        host: 'hooks.slack.com',
        port: 443,
        method: 'POST',
        path: '{{ctx.metadata.slackUrlPath}}',
        headers: {
          'Content-Type': 'application/json'
        },
        body: `__json__::${JSON.stringify({
          text: slackTemplate
        })}`
      }
    };
  }

  if (!isEmpty(emails)) {
    body.actions.email = {
      email: {
        to: '{{#join}}ctx.metadata.emails{{/join}}',
        subject: i18n.translate(
          'xpack.apm.serviceDetails.enableErrorReportsPanel.emailSubjectText',
          {
            defaultMessage:
              '{serviceName} has error groups which exceeds the threshold',
            values: { serviceName: '"{{ctx.metadata.serviceName}}"' }
          }
        ),
        body: {
          html: emailTemplate
        }
      }
    };
  }

  await createWatch(id, body);
  return id;
}