|
16 | 16 | */ |
17 | 17 | package org.graylog.integrations.pagerduty.client; |
18 | 18 |
|
| 19 | +import com.floreysoft.jmte.Engine; |
19 | 20 | import com.google.common.collect.ImmutableList; |
| 21 | +import jakarta.inject.Inject; |
| 22 | +import jakarta.inject.Named; |
20 | 23 | import org.apache.commons.lang3.StringUtils; |
21 | 24 | import org.graylog.events.notifications.EventNotificationContext; |
22 | 25 | import org.graylog.events.notifications.EventNotificationModelData; |
23 | 26 | import org.graylog.events.notifications.EventNotificationService; |
24 | | -import org.graylog.events.processor.EventDefinitionDto; |
25 | | -import org.graylog.events.processor.aggregation.AggregationEventProcessorConfig; |
| 27 | +import org.graylog.events.notifications.TemplateModelProvider; |
26 | 28 | import org.graylog.integrations.pagerduty.PagerDutyNotificationConfig; |
27 | 29 | import org.graylog.integrations.pagerduty.dto.Link; |
28 | 30 | import org.graylog.integrations.pagerduty.dto.PagerDutyMessage; |
29 | 31 | import org.graylog2.plugin.MessageSummary; |
30 | | -import org.graylog2.plugin.streams.Stream; |
31 | | -import org.graylog2.streams.StreamService; |
32 | | - |
33 | | -import jakarta.inject.Inject; |
| 32 | +import org.graylog2.web.customization.CustomizationConfig; |
| 33 | +import org.joda.time.DateTimeZone; |
34 | 34 |
|
35 | 35 | import java.net.MalformedURLException; |
36 | | -import java.net.URL; |
| 36 | +import java.net.URI; |
| 37 | +import java.net.URISyntaxException; |
37 | 38 | import java.util.Arrays; |
38 | 39 | import java.util.HashMap; |
39 | 40 | import java.util.List; |
40 | 41 | import java.util.Locale; |
41 | 42 | import java.util.Map; |
42 | | -import java.util.stream.Collectors; |
43 | 43 |
|
44 | 44 | /** |
45 | 45 | * Factory class for PagerDuty messages, heavily based on the works of the cited authors. |
|
51 | 51 | * @author Edgar Molina |
52 | 52 | */ |
53 | 53 | public class MessageFactory { |
54 | | - private static final List<String> PAGER_DUTY_PRIORITIES = Arrays.asList("info", "warning", "critical"); |
| 54 | + private static final List<String> PAGER_DUTY_PRIORITIES = Arrays.asList("info", "warning", "critical", "critical"); |
55 | 55 |
|
56 | | - private final StreamService streamService; |
57 | 56 | private final EventNotificationService eventNotificationService; |
| 57 | + private final CustomizationConfig customizationConfig; |
| 58 | + private final Engine templateEngine; |
| 59 | + private final TemplateModelProvider templateModelProvider; |
58 | 60 |
|
59 | 61 | @Inject |
60 | | - MessageFactory(StreamService streamService, EventNotificationService eventNotificationService) { |
61 | | - this.streamService = streamService; |
| 62 | + MessageFactory(EventNotificationService eventNotificationService, |
| 63 | + CustomizationConfig customizationConfig, |
| 64 | + @Named("JsonSafe") Engine jsonTemplateEngine, |
| 65 | + TemplateModelProvider templateModelProvider) { |
62 | 66 | this.eventNotificationService = eventNotificationService; |
| 67 | + this.customizationConfig = customizationConfig; |
| 68 | + this.templateEngine = jsonTemplateEngine; |
| 69 | + this.templateModelProvider = templateModelProvider; |
63 | 70 | } |
64 | 71 |
|
65 | 72 | public PagerDutyMessage createTriggerMessage(EventNotificationContext ctx) { |
66 | 73 | final ImmutableList<MessageSummary> backlog = eventNotificationService.getBacklogForEvent(ctx); |
67 | 74 | final EventNotificationModelData modelData = EventNotificationModelData.of(ctx, backlog); |
68 | 75 | final PagerDutyNotificationConfig config = (PagerDutyNotificationConfig) ctx.notificationConfig(); |
| 76 | + final Map<String, Object> messageModel = getCustomMessageModel(ctx, backlog); |
69 | 77 |
|
70 | | - String eventTitle = modelData.eventDefinitionTitle(); |
| 78 | + final String eventTitle = config.pagerDutyTitle() |
| 79 | + .map(customTitle -> templateEngine.transform(customTitle, messageModel)) |
| 80 | + .orElse(modelData.eventDefinitionTitle()); |
71 | 81 | String eventPriority = PAGER_DUTY_PRIORITIES.get(0); |
72 | 82 | int priority = ctx.eventDefinition().get().priority() - 1; |
73 | | - if (priority >= 0 && priority <= 2) { |
| 83 | + if (priority >= 0 && priority <= 3) { |
74 | 84 | eventPriority = PAGER_DUTY_PRIORITIES.get(priority); |
75 | 85 | } |
76 | 86 |
|
77 | | - List<Link> streamLinks = |
78 | | - streamService |
79 | | - .loadByIds(modelData.event().sourceStreams()) |
80 | | - .stream() |
81 | | - .map(stream -> buildStreamWithUrl(stream, ctx, config)) |
82 | | - .collect(Collectors.toList()); |
| 87 | + final List<Link> replayLink; |
| 88 | + try { |
| 89 | + final String replayUrl = StringUtils.appendIfMissing( |
| 90 | + config.clientUrl(), "/") + "alerts/" + modelData.event().id() + "/replay-search"; |
| 91 | + replayLink = List.of(new Link(new URI(replayUrl).toURL(), "Replay Event")); |
| 92 | + } catch (URISyntaxException | MalformedURLException e) { |
| 93 | + throw new IllegalStateException("Error when building the event replay URL.", e); |
| 94 | + } |
83 | 95 |
|
84 | 96 | String dedupKey = ""; |
85 | 97 | if (config.customIncident()) { |
86 | | - dedupKey = String.format(Locale.ROOT, |
87 | | - "%s/%s/%s", config.keyPrefix(), modelData.event().sourceStreams(), eventTitle); |
| 98 | + final String formattedPrefix = templateEngine.transform(config.keyPrefix(), messageModel); |
| 99 | + final String prefixedIncidentKey = String.format(Locale.ROOT, |
| 100 | + "%s/%s/%s", formattedPrefix, modelData.event().sourceStreams(), eventTitle); |
| 101 | + // Use the custom incident key if provided, otherwise fall back to the prefixed key. |
| 102 | + dedupKey = config.incidentKey() |
| 103 | + .map(incidentKeyTemplate -> templateEngine.transform(incidentKeyTemplate, messageModel)) |
| 104 | + .orElse(prefixedIncidentKey); |
88 | 105 | } |
89 | 106 |
|
90 | | - |
91 | | - Map<String, String> payload = new HashMap<String, String>(); |
92 | | - payload.put("summary", modelData.event().message()); |
93 | | - payload.put("source", "Graylog:" + modelData.event().sourceStreams()); |
| 107 | + Map<String, Object> payload = new HashMap<>(); |
| 108 | + payload.put("summary", config.pagerDutyTitle() |
| 109 | + .map(customTitle -> templateEngine.transform(customTitle, messageModel)) |
| 110 | + .orElse(modelData.event().message())); |
| 111 | + payload.put("source", customizationConfig.productName() + ":" + modelData.event().sourceStreams()); |
94 | 112 | payload.put("severity", eventPriority); |
95 | 113 | payload.put("timestamp", modelData.event().eventTimestamp().toString()); |
96 | 114 | payload.put("component", "GraylogAlerts"); |
97 | 115 | payload.put("group", modelData.event().sourceStreams().toString()); |
98 | 116 | payload.put("class", "alerts"); |
| 117 | + payload.put("custom_details", ctx.event().fields()); |
99 | 118 |
|
100 | 119 | return new PagerDutyMessage( |
101 | 120 | config.routingKey(), |
102 | 121 | "trigger", |
103 | 122 | dedupKey, |
104 | 123 | config.clientName(), |
105 | 124 | config.clientUrl(), |
106 | | - streamLinks, |
| 125 | + replayLink, |
107 | 126 | payload); |
108 | 127 | } |
109 | 128 |
|
110 | | - private Link buildStreamWithUrl(Stream stream, EventNotificationContext ctx, PagerDutyNotificationConfig config) { |
111 | | - final String graylogUrl = config.clientUrl(); |
112 | | - String streamUrl = |
113 | | - StringUtils.appendIfMissing(graylogUrl, "/") + "streams/" + stream.getId() + "/search"; |
114 | | - |
115 | | - if (ctx.eventDefinition().isPresent()) { |
116 | | - EventDefinitionDto eventDefinitionDto = ctx.eventDefinition().get(); |
117 | | - if (eventDefinitionDto.config() instanceof AggregationEventProcessorConfig) { |
118 | | - String query = |
119 | | - ((AggregationEventProcessorConfig) eventDefinitionDto.config()).query(); |
120 | | - streamUrl += "?q=" + query; |
121 | | - } |
122 | | - } |
123 | | - try { |
124 | | - return new Link(new URL(streamUrl), stream.getTitle()); |
125 | | - } catch (MalformedURLException e) { |
126 | | - throw new IllegalStateException("Error when building the stream link URL.", e); |
127 | | - } |
| 129 | + private Map<String, Object> getCustomMessageModel(EventNotificationContext ctx, List<MessageSummary> backlog) { |
| 130 | + return templateModelProvider.of(ctx, backlog, DateTimeZone.UTC, Map.of("type", PagerDutyNotificationConfig.TYPE_NAME)); |
128 | 131 | } |
129 | 132 | } |
0 commit comments