Using Handlebars templates with Seq.App.Atlassian.Jira and Seq.App.OpsGenie

One of the really useful things with Seq.App.Opsgenie was that Nick Blumhardt had integrated Handlebars templates to the app, using Handlebars.NET. I liked it so much that, when I contributed to Ali Özgür's excellent Seq.App.Atlassian.Jira project, I carried it over.

There's a few oddities in the JIRA REST API, and interacting with it via user-entered data in a C# application can make it complex. It particularly doesn't handle Wiki markup like \n and \t as well as you might expect. Updating the Jira Description property to a "LongText" input type in Seq.App.Atlassian.Jira helps quite a bit, since you can enter a multi-line config that works more as you expected - tables and headers, for example, will properly render. We might need to circle back and do that for Seq.App.OpsGenie, so that editing templates is neater at the least.

The latest version of Seq.App.Atlassian.Jira fixes the linebreak issue below - \n and \r will now work as expected. For the sake of compatibility, \\ and \\\\ are still accepted.

The only oddity left is that, if we want to explicitly call for a linebreak, we need to express it as the Wiki format "\\" - but thanks to the interaction between Seq, the Jira app, and the REST API, we need to escape it, and put it on a new line or with spaces between it and the other content. So to make a line break, you need to put " \\\\ " for it to work as expected.

What I love about having Handlebars templates is that you get access to the inbuilt "if" and "each" statements. So if you want a simple Jira issue which shows the event message and then each property of the event as a table, you can do;

Jira Summary[Seq] - ({{$Level}}) {{$Message}}
Jira Description{{$Message}}
\n \n
{{#each $Properties}}|| {{@key}} : | {{this}} ||
Alert created by Seq

And this will give you a very nicely formatted Key: and Value table.

Of course, you can get quite fancy with this, because Handlebars gives you access to any property of the event. The "each" statement gives you a quick way to list these in a generic table or other format, but you can leverage each property directly and even use the "if" statement to only include parts if the value exists.

Jira Summary[Seq] - ({{Priority}}) {{Summary}}
Jira Description

h2. {{Description}}
\n \n
|| Property 1: | {{Property1}} ||
|| Property 2: | {{Property2}} ||
|| Property 3: | {{Property3}} ||
|| Property 4: | {{Property4}} ||
*Alert created by [Seq|{{$EventUri}}]*

So much more capability and power is possible here - not just tables, but all kinds of formatting and structure!

Because Event Schedule for Seq is well placed to create tickets in Jira, I've updated its Scheduled log description property to allow the LongText setting type that permits multi-line inputs. Event Schedule doesn't use Handlebars templating (but does allow some and log token and date tags) but you can use this to create scheduled tickets that pass Jira formats through nicely.

This provides a Description property that can simply be passed through to Jira via Seq.App.Atlassian.Jira, so normal line feeds will work. An example of this is as follows:

Scheduled Log Message{LogToken} - {LogTokenLong} - {MMMM yyyy-1m} - Review
Scheduled Log DescriptionPlease review the effectiveness of the IT Control "{LogTokenLong}" for *{MMMM yyyy-1m}*.

This review looks at the evidence from the previous month ({MMM yy-1m}) to validate  that controls remain effective.
*Ensure that evidence is linked or attached, and that this clearly shows the effectiveness of the control.*

Within the OpsGenie app, Nick had added access to the Seq event properties by way of tags that were prefixed with $, and I had extended to include a {{$EventUri}} tag that provided simple access to the URL for the event itself. I retained those for the Jira app.

                {"$Id", evt.Id},
                {"$UtcTimestamp", evt.TimestampUtc},
                {"$LocalTimestamp", evt.Data.LocalTimestamp},
                {"$Level", evt.Data.Level},
                {"$MessageTemplate", evt.Data.MessageTemplate},
                {"$Message", evt.Data.RenderedMessage},
                {"$Exception", evt.Data.Exception},
                {"$Properties", properties},
                {"$EventType", "$" + evt.EventType.ToString("X8")},
                {"$Instance", host.InstanceName},
                {"$ServerUri", host.BaseUri},
                // Note, this will only be valid when events are streamed directly to the app, and not when the app is sending an alert notification.
                    string.Concat(host.BaseUri, "#/[email protected]%20%3D%20'", evt.Id, "'&show=expanded")

An oddity between OpsGenie and Jira - both Atlassian properties - is that OpsGenie accepts HTML markup and I can't find any indication that Wiki markup is acceptable, while of course Jira only accepts Wiki markup. This means that if OpsGenie creates Jira tickets from your Seq alerts, and you've used HTML, you might get some oddly formatted JIra tickets. 

An OpsGenie alert config in the current version of Seq.Apps.OpsGenie might look like:

Alert message[Seq] - {{$Level}}: {{AlertTitle}} - {{Condition}}
Alert description

<p>{{$Level}} - {{AlertTitle}}<br /><br /></p><p><table>{{#each $Properties}}<tr><td><b>{{@key}}:</b></td><td>{{this}}</td></tr>{{/each}}</table></p><p>Alert created by Seq</p>

which is going to be a mess in Jira. To address this, we use Automation for Jira Lite to translate our HTML alerts to Opsgenie into Jira markup.

This takes the form of an "Edit issue fields" on the "Description" field - I show the rule below, but we're leveraging Smart Values functionality to do the following;

{{issue.description.replaceAll("<br />","\n").replaceAll("<b>", "*").replaceAll("</b>","*").replaceAll("<p>","").replaceAll("</p>","\n\n").replaceAll("<table>","").replaceAll("<tr>","|").replaceAll("</tr>","|\n").replaceAll("</td><td>","|").replaceAll("<td>","|").replaceAll("</td>","|").replaceAll("</table>","")}}

This is a neat translation based on only updating HTML fields that we use within our Handlebars template for OpsGenie.

Automation for Jira rules to replace HTML with Jira wiki format

I hope this is of some assistance in getting people started with nicely formatted templates in Seq, and in translating OpsGenie HTML to Jira!




You may also like:

Lurgle.Alerting - a standardised FluentEmail implementation with extra goodies!

Another Lurgle Around the time that I tackled my original Serilog logging implementation, I also looked at our email alerting. Emails can be used for a variety of reasons, and it's not uncommon that they are sent as a simple string that concatenates or formats variables. In this scenario, the...

Lurgle.Logging - a standardised Serilog implementation with extra goodies!

Logging is important Logging is a really important, oft-neglected, aspect of business applications. I can't state that enough. If you don't have good logging, you can't troubleshoot and debug problems, and you have little chance of seeing what's actually going on in your enterprise. In Structured Logging with Seq and Serilog,...

Calculating timeouts with Event Timeout for Seq

We use quite a number of Event Timeout instances in our Seq environment, to detect processes that have not completed in time. The nature of the Seq.App.EventTimeout implementation is one that relies on a timeout in seconds, and this can result in keeping track of quite a few different calculations....