summaryrefslogtreecommitdiff
path: root/app/presenters/projects/prometheus/alert_presenter.rb
blob: 28b8cd6522c929f19ce54230b726033424218eca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# frozen_string_literal: true

module Projects
  module Prometheus
    class AlertPresenter < Gitlab::View::Presenter::Delegated
      RESERVED_ANNOTATIONS = %w(gitlab_incident_markdown gitlab_y_label title).freeze
      GENERIC_ALERT_SUMMARY_ANNOTATIONS = %w(monitoring_tool service hosts).freeze
      MARKDOWN_LINE_BREAK = "  \n".freeze
      INCIDENT_LABEL_NAME = IncidentManagement::CreateIncidentLabelService::LABEL_PROPERTIES[:title].freeze
      METRIC_TIME_WINDOW = 30.minutes

      def full_title
        [environment_name, alert_title].compact.join(': ')
      end

      def project_full_path
        project.full_path
      end

      def metric_query
        gitlab_alert&.full_query
      end

      def environment_name
        environment&.name
      end

      def performance_dashboard_link
        if environment
          metrics_project_environment_url(project, environment)
        else
          metrics_project_environments_url(project)
        end
      end

      def show_performance_dashboard_link?
        gitlab_alert.present?
      end

      def show_incident_issues_link?
        project.incident_management_setting&.create_issue?
      end

      def incident_issues_link
        project_issues_url(project, label_name: INCIDENT_LABEL_NAME)
      end

      def start_time
        starts_at&.strftime('%d %B %Y, %-l:%M%p (%Z)')
      end

      def issue_summary_markdown
        <<~MARKDOWN.chomp
          #### Summary

          #{metadata_list}
          #{alert_details}#{metric_embed_for_alert}
        MARKDOWN
      end

      def annotation_list
        strong_memoize(:annotation_list) do
          annotations
            .reject { |annotation| annotation.label.in?(RESERVED_ANNOTATIONS | GENERIC_ALERT_SUMMARY_ANNOTATIONS) }
            .map { |annotation| list_item(annotation.label, annotation.value) }
            .join(MARKDOWN_LINE_BREAK)
        end
      end

      def metric_embed_for_alert
        url = embed_url_for_gitlab_alert || embed_url_for_self_managed_alert

        "\n[](#{url})" if url
      end

      private

      def alert_title
        query_title || title
      end

      def query_title
        return unless gitlab_alert

        "#{gitlab_alert.title} #{gitlab_alert.computed_operator} #{gitlab_alert.threshold} for 5 minutes"
      end

      def metadata_list
        metadata = []

        metadata << list_item('Start time', start_time) if start_time
        metadata << list_item('full_query', backtick(full_query)) if full_query
        metadata << list_item(service.label.humanize, service.value) if service
        metadata << list_item(monitoring_tool.label.humanize, monitoring_tool.value) if monitoring_tool
        metadata << list_item(hosts.label.humanize, host_links) if hosts

        metadata.join(MARKDOWN_LINE_BREAK)
      end

      def alert_details
        if annotation_list.present?
          <<~MARKDOWN.chomp

            #### Alert Details

            #{annotation_list}
          MARKDOWN
        end
      end

      def list_item(key, value)
        "**#{key}:** #{value}".strip
      end

      def backtick(value)
        "`#{value}`"
      end

      GENERIC_ALERT_SUMMARY_ANNOTATIONS.each do |annotation_name|
        define_method(annotation_name) do
          annotations.find { |a| a.label == annotation_name }
        end
      end

      def host_links
        Array(hosts.value).join(' ')
      end

      def embed_url_for_gitlab_alert
        return unless gitlab_alert

        metrics_dashboard_project_prometheus_alert_url(
          project,
          gitlab_alert.prometheus_metric_id,
          environment_id: environment.id,
          **alert_embed_window_params(embed_time)
        )
      end

      def embed_url_for_self_managed_alert
        return unless environment && full_query && title

        metrics_dashboard_project_environment_url(
          project,
          environment,
          embed_json: dashboard_for_self_managed_alert.to_json,
          **alert_embed_window_params(embed_time)
        )
      end

      def embed_time
        starts_at || Time.current
      end

      def alert_embed_window_params(time)
        {
          start: format_embed_timestamp(time - METRIC_TIME_WINDOW),
          end: format_embed_timestamp(time + METRIC_TIME_WINDOW)
        }
      end

      def format_embed_timestamp(time)
        time.utc.strftime('%FT%TZ')
      end

      def dashboard_for_self_managed_alert
        {
          panel_groups: [{
            panels: [{
              type: 'line-graph',
              title: title,
              y_label: y_label,
              metrics: [{
                query_range: full_query
              }]
            }]
          }]
        }
      end
    end
  end
end