Starting with version 11.2, the GitHub releases page is the place where release notes are found.
Release highlights
What’s new in version 11.1.0
π Features
license: Monitor high level Qilk Sense license usage across different license types.
license: Scheduled removal of unused user Qlik Sense licenses.
π Bug Fixes
config: Better, more complete check of config when starting Butler.
reload failed: Make handling of reload failed/aborted/succeeded messages more robust.
startup: Remove Node.js warnings on Butler startup.
startup: More consistent logging during startup.
startup: Tidy up formatting of startup info written to logs.
π Miscellaneous
Optimize GH Actions for building binaries.
Remove udp client from Butler project, move to its own repo.
Sign Win binaries with new signing solution.
Update dependencies.
What’s new in version 11.0.2
This is a maintenance release used to test the automatic building of Butler binaries, Docker images etc.
No new features or bug fixes were added in this version.
What’s new in version 11.0.1
This is a maintenance release used to test the automatic building of Butler binaries, Docker images etc.
No new features or bug fixes were added in this version.
What’s new in version 11.0.0
Focus of this release is to modernize the Butler code base.
No new features, but a lot of work has been done to make Butler more robust and easier to maintain in the future.
π Bug Fixes
docker: Correctly report Docker status (72e1087), closes #939
general: Make path resoultion for QIX schema files more robust (568aa2e)
mqtt: Better logging and check for cert existence (59dc4fa)
webhook: Deal with empty webhook list wo errors (3f42d02), closes #944
winsvc: Win service monitoring no longer rely on New Relic (e47124c), closes #967
What’s new in version 9.4.0
π Features
influxdb: Add Butler version tag to uptime data sent to InfluxDb.
π Bug Fixes
winsvc: Improve Winsvc checking efficiency
What’s new in version 9.3.2
βοΈ This release address several bugs related to Windows service monitoring.
Long story short - this should work a lot better now compared to previous versions.
π A bonus is that the Windows service monitoring is significantly faster than before.
For example, if you monitor 5 services on a server, Butler’s monitoring code now executes ca 5 times faster than before!
π Bug Fixes
mqtt: Don’t show MQTT startup info when MQTT is disabled.
winsvc: Bug fixes and better logging for win service monitoring.
winsvc: Make Windows service status checks quicker.
Miscellaneous
deps: Update dependencies to stay safe & secure.
What’s new in version 9.3.1
Bug Fixes
mqtt: More reboust startup code for MQTT & Win svc monitoring.
winsvc: Don’t send Win svc alerts when Butler starts.
Refactoring
logging: More consistent log prefixes.
What’s new in version 9.3.0
Features
influxdb: Store failed reload info in InfluxDB.
mqtt: Add support for Azure Event Grid as MQTT broker.
influxdb: Store reload task success info in InfluxBD.
Bug Fixes
Disable SMTP mail appender in sample config.
More robust generation of anonymous Butler instance id.
mqtt: Better error handling when establishing MQTT connection.
Verify that all required config file entries exist.
winservice: Better handling of services that don’t exist.
Miscellaneous
Add sample config files to release ZIPs.
What’s new in version 9.2.0
Features
reload-alerts: Make app owner info available in reload failed alerts.
What’s new in version 9.1.0
Features
scrip logs: Only get failed-reload script logs once from Sense server.
telemetry: Change to using PostHog for telemetry collection.
What’s new in version 9.0.0
β BREAKING CHANGES
Move InfluxDB settings to their own section in config file
Features
Add InfluxDB as destination for Windows service status monitoring
Add monitoring of Windows services
Log at startup current API rate limit
Log at startup which config file is used
Log warnings when API rate limits exceeded
Move InfluxDB settings to their own section in config file
New command line option for setting API rate limit
Verify structure of config file on Butler startup
Bug Fixes
Add missing fields to template config file
Only initiate InfluxDB connection if it’s actually enabled in config file
Only set up REST server if it’s actually enabled in the config file
Other
deps: Update dependencies to stay safe & secure
Update Docker image to use Node.js v20
What’s new in version 8.6.2
Other
deps: Update dependencies to stay safe & secure
What’s new in version 8.6.1
Bug Fixes
Allow empty New Relic settings in config file’s uptime section
Allow uptime reporting to New Relic without custom http headers
Better log messages when rate limiting for reload notifications passes
Config asset errors when starting Butler without any New Relic accounts specified
Improve warning when custom property names in config file don’t exist in Sense
Only send to New Relic if event/log is enabled AND custom property name specified.
Upgrade Swagger docs to latest version
Other
deps: Update dependencies to stay safe & secure
What’s new in version 8.6.0
Features
Add virus/malware scanning of standalone binaries during build,
Sign Windows binaries during build
Other
deps: Update dependencies to stay safe & secure
What’s new in version 8.5.3
Other
Update dependencies
What’s new in version 8.5.2
Bug Fixes
Handle startup error messages without… errors
Improved startup checks of custom properties handling New Relic destinations
Incorrect error messages in config assert module
Other
deps: Updated dependencies to stay safe and secure
What’s new in version 8.5.1
Bug Fixes
New Relic CLI options now work again
Other
deps: Updated dependencies to stay safe and secure
What’s new in version 8.5.0
This release enhances the integration between Butler and the New Relic SaaS monitoring platform.
Specifically, it’s now possible to control per reload task which New Relic account failed/aborted task notifications should be sent to.
The destination New Relic account(s) (where failed reload events/log entries are sent) is set via custom properties on the reload tasks.
Failed and aborted tasks can be sent to New Relic as events or log entries, or both.
Zero, one or more New Relic accounts can be defined in the Butler config file.
Features
new-relic: Allow per-reload-task control of to which New Relic account failed/aborted reload alerts are sent
Add new command line option –no-qs-connection. Used when no connection to Sense should be done, for example when generating API docs
Bug Fixes
scriptlog: Increase timeout when getting script logs
scriptlog: More descriptive messages when script log retrieval fails
Other
deps: Update dependencies to latest versions
What’s new in version 8.4.2
Bug Fixes
Add building of Linux binaries to build pipeline
What’s new in version 8.4.1
Bug Fixes
Properly store demo apps in GitHub
What’s new in version 8.4.0
Features
Make file copy/move/delete REST endpoints more robust
Warn if UNC paths are used with file API calls, when Butler runs on non-Windows OS
Bug Fixes
Make startup logging of approved directories for file copy/move/delete less verbose
Other
Update dependencies
What’s new in version 8.3.3
Bug Fixes
API endpoint /v4/schedules/status now respects enable/disable in config file #509
Incorrect return value from base conversion API endpoint #508
Incorrect telemetry status (true/false) for uptime data sent to InfluxDB. #401
Other
Update dependencies to stay sharp and secure.
Now using Node.js v18 when building Docker images.
What’s new in version 7.2
Features
Create stand-alone binaries to make it easier to get started with Butler. No need to install Node.js any more, just download Butler, configure it and run. #383
Make a few important config options available as command line parameters. Specifically, the config file to use and the log level can be specified via command line options --configfile and --loglevel, respectively. #381
Store full reload logs for failed reload tasks to disk, for easier analysis at later time. #94
Bug fixes
Better error checking when calling Qlik Sense APIs. #386
Better handling of long script logs in notifications sent to MS Teams. #389
Better handling of long script logs in notifications sent to Slack. #388
Other
What’s new in version 7.1
Features
Add control of what tasks can be started via Butler’s REST API. Also known as “task filtering”. #284
api: Verify that task IDs are valid (both that they are valid guids and that they exist in Sense) before trying to start the associated tasks. #319
Refactor API for starting tasks. Add magic task guid “-” that can be used as URL parameter when all task IDs (and other parameters) are passed in via the message body. #326
Show URL to API docs/Swagger page on Butler startup. #317
Bug fixes
api: API calls with http “Expect” header no longer fails. #322
Increase timeout in API test cases from 5 to 15 seconds. This gets rid of occasional timeouts in the test suite. #329
File copy/move APIs now respect options preserveTimestamp and overwrite. #263
Return proper JSON from successful API calls. Refactoring the API code in v6.0 introduced a bug, causing empty responses from successful calls to some API endpoints. #260
Added a new API endpoint for listing all keys in a key-value namespace. Β #150.
Fixed a bug where Butler would not start properly if there were empty config sections in the YAML config file. Butler is now more tolerant against slightly incorrectly formatted config files. #152.
What’s new in version 5.2
It’s now possible to include zero or more (i.e. optional) key-value pairs when starting QSEoW reload tasks using the /v4/reloadtask/{taskId}/start REST endpoint.
There are also helper subs available in the demo app included in the GitHub repository - as well as in the online docs at butler.ptarmiganlabs.com. (#147, #148)
What’s new in version 5.1
First version of telemetry added to Butler (#142). More info here.
Show high level system info when starting Butler (#140).
Don’t waste memory when MQTT is not used: Fixed #139.
Refined the documentation, fixed typos and updated dependencies. The usual stuff that comes with every release.
What’s new in version 5.0
Greatly improved failed reload notifications for both MS Teams and Slack.
Task failure alerts for these channels now use the same advanced templating solution that’s already available for Butler’s email notifications. This is in many ways a milestone as it brings a new level of reload alerts to Qlik Sense Enterprise.
The downside is that the Slack and Teams settings in Butler’s config file have been changed in a breaking way - make sure to update the config file as needed when upgrading Butler.
Due to the breaking nature of the config file changes, the version number was bumped to 5.0 rather than 4.4.
Added a new API endpoint for doing app reloads. Both full and partial reloads are supported. When the app has reloaded, a one or more reload tasks can be started based on whether the app reload completed successfully or not.
Added new API endpoints for listing all apps in the Sense server, as well as extracting metadata for a specific app. Those features have been available in Butler for many years, they just get a couple of new endpoints that better align with Butler’s current API naming standard.
Refined user session monitoring. The previous XML appender file provided by Butler was too generous and passed on too many user activity events to Butler. The new appender files provide a tighter/more relevant filtering and only returns session start/stop events to Butler (from which this info can be sent to Slack or MS Teams).
What’s new in version 4.3
Fixed a bug that in some cases prevented Sense reload tasks from being started using Butler’s API.
Added instruction for creating the data connections needed to use Butler APIs from Sense load scripts.
Added concept page explaining how Butler can be used to copy/move/delete files from load scripts.
Added example page showing how file manipulation (copy/move/delete) is done from Sense scripts, using Butler’s APIs.
What’s new in version 4.2
Email reload alerts taken to a new level. Emails are created using template files, with full support for HTML formatting, emoji support in both email subject/body etc.
This makes it possible to create nice looking alert emails that contain just the information you need, while also conforming to corporate design, colors etc.
Alert emails can be set up for either failed scheduled reloads, or running reloads that are stopped from the QMC or via APIs. Or both.
More than 40 templating fields available, including task history (what steps the task has been through), beginning and end of reload script log, app/task metadata, 5+ date formats and more.
Template fields can be used in both subject and body of email.
Rate limiter that prevents email spamming for frequently failing tasks.
What’s new in version 4.1
The REST API now lets you copy files in a controlled, secure way. Version 4.0 added similar features for file moving and deletes, so copying is a natural complement.
What’s new in version 4.0
An advanced scheduler that makes it possible to trigger reloads in Sense in a much more flexible way, compared to the scheduler available in the Qlik Sense QMC. It’s essentially Cron for Qlik Sense.
A generic key-value store can be used to send parameters between reload tasks. The reload script of the first task stash it’s parameters away in the KV store, and the following task(s) pull the parameters from the store.
KV pairs can optionally also have a time-to-live (TTL) value. This can be an easy way to keep the KV store clean and tidy, or as a way to tell Sense apps that they are reloading within x hours of some event happening, for example.
Key-value stores are a very versatile tool and a great addition to Qlik Sense.
Moving and deleting files in the file system of the Sense servers, or on any disks accessible by those servers. You customize what directories are approved for these operations, and thus prevent this feature from becoming a security risk.
Reload failure notifications can now be sent to Microsoft Teams, in addition to Slack and as email.
Better logging, including continuous logging of Butler’s own memory usage to InfluxDB, from where it can be graphed using for example Grafana.
API docs using the OpenAPI/Swagger format.
A totally re-engineered REST API that now better follows best practices when to use GET/POST/PUT/DELETE. Previously everything was done using GETs… which was really ugly. The downside is that this is a major breaking change! Please review the API docs for details.
1 - About
Information about the Butler software, community, docs and more.
Are you stuck on something while setting up Butler? Got ideas for new features?
Don’t hesitate to post your thoughts in the Butler forums.
1.1 - Butler
An introduction to Butler.
The Butler project is all about adding useful features to both the client-managed version of Qlik Sense, also known as Qlik Sense Enterprise on Windows (QSEoW) and Qlik Sense Cloud.
Some of the features can be used from Sense load scripts, other features provide integration with 3rd party systems.
Most of Butler’s features simply try to make daily life for a Qlik Sense administrator or developer a bit easier.
Given Butler’s history and the fact that it was originally developed for Qlik Sense Enterprise on Windows, most features are only available for that platform.
More and more Qlik Sense Cloud features are being added, for example the ability to send app reload failed alerts to email, Slack, Teams or script log on local disk.
The goal is to integrate battle-proven concepts and best-of-breed open-source tools into Butler and thus make them available to developers of Sense apps or those responsible for running Sense clusters.
In some cases it might be possible to use these tools from within Sense also without Butler (for example sending messages to Teams or Slack) - in those cases Butler simply tries to make things easier, lowering the barriers to get started and get things done.
There is also a clear goal that Butler should be very configurable. In practice this means that individual features can be turned on/off as needed, improving security and lowering memory usage.
Butler is written in Node.js and runs on most modern operating systems.
There are pre-built binaries for Windows, macOS and Linux, meaning that Node.js does not have to be separately installed to use Butler.
This simplifies things - just download, configure and run.
You can run Butler on the same server as Qlik Sense, in a Docker container on a Linux server, in Kubernetes, on Mac OS, on Raspberry Pi (not a good idea.. but possible and proven to work).
Butler is a member of a group of tools collectively referred to as the “Butler family”, more info is available here.
This picture might be useful to understand what Butler does and how it fits into the larger system map around Qlik Sense:
Instant notifications when reload tasks fail or are stopped
Information about failing tasks can be sent as emails, to Microsoft Teams, Slack, as MQTT messages or outgoing webhooks.
Tip
This feature is available for both Qlik Sense client-managed and Qlik Sense Cloud.
Email, Slack and MS Teams notifications all use a templating concept where HTML/Markdown template files describe what the alert message should look like. Before the alert is sent the template is populated with actual data from the failed reload task (client-managed Sense) or app reload (Sense Cloud).
For email alerts both subject and body of the email can be templated.
For both Slack and Teams there are options to use more flexible/configurable alert formats and more basic pre-configured alerts.
The result is a very poweful tool for both QSEoW sysadmins and those responsible for Qlik Cloud tenants, who both want to be notified when reloads fail.
Forward failed reload events to incident management systems (New Relic, Signl4)
Butler offers advanced failed reload alerts via Slack, Teams, email and outgoing webhooks.
Configurable templates means you can customize emails/Teams/Slack messages.
Sometimes you want a bit more structure though.
This is especially true when Sense is used in the enterprise.
Butler integrates with both Signl4 and New Relic.
Both offer incident management features on both the web and via mobile clients.
Information about failed/aborted reloads can be sent to one or more New Relic accounts.
Tags for the reload task and associated app is sent to New Relic as metadata for the event/log entry that’s created there.
Send alerts when reload tasks succeed
Knowing about failed reloads is important, but sometimes it’s just as important to know when a reload has succeeded.
Get emails when those important reloads have completed successfully.
Nicely formatted with all the details you need.
Controlling which tasks should send success alerts is done using custom properties or via Butler’s config file.
Use InfluxDB/Grafana or New Relic to track Butler memory usage
Butler can be configured to log its own memory usage to InfluxDB, from where it can be visualised using Grafana.
If you prefer using New Relic One that’s possible too - sending Butler memory metrics to New Relic is super simple: Just add your New Relic credentials in the YAML config file or as command line options when starting Butler and you’re set.
Save a copy of the complete reload log for all failed reload tasks
Tip
This feature is available for both Qlik Sense client-managed and Qlik Sense Cloud.
Let’s say a scheduled reload task fails.
This can happen due to lots of reasons, from uncontrollable events that are impossible to predict to bugs in the script of a Sense app.
No matter what the cause is, as a Sense administrator you probably want to investigate the script reload logs.
Butler can send notifications (Slack, Teams, email, webhooks, …) when reloads fail.
These notifications can include the last 20-30-40 lines of the script log and this usually gives a good idea of what caused the reload to fail.
But what if you want to look at the complete reload log of that failed app reload?
So far you would have to dig into the log directory on the Sense server (for client-managed Sense) and find that specific reload log file among potentially thousands of other log files. Not very effective.
For Sense Cloud you would have to download the log file from the Sense Cloud hub. Doable, but could be easier.
Butler can store a copy of the complete reload log of failed reload tasks/app reloads in directories that you specify.
The log files are stored in separate directories, one for each date. Sense Cloud logs are stored in a separate directory tree from client-managed Sense logs.
This makes it easy to find the log file you are interested in.
Start reload tasks from load script or from upstream systems
Trigger Sense reload tasks from a reload script: This makes it possible to start different Sense tasks based on what data has been read from a database, what time of day it is etc.
Starting a task from the reload script is as easy as Call StartTask('fbf645f0-0c92-40a4-af9a-6e3eb1d3c35c').
Trigger Sense reloads from external systems: When new data is available in a source database, that database can trigger a reload in Sense, and the data is loaded from the database into Sense. This way delays caused by Sense polling for data are minimized and data arrives at end users as quickly as possible.
Starting reload tasks using REST API is described here.
Using MQTT messages to achieve this is described here.
Start any reload task from within any Qlik Sense or web app
Some HTML and Javascript magic is also needed, but given Butler’s start-task API it’s pretty easy to set up a button in a Sense app (or any web app!) to start any Sense reload task.
This can for example be used to allow end users to start an Extract-Transform when they (the user) need refreshed data.
Start reload tasks via REST API based on task tags or custom properties
Using tags and/or custom properties to identify what tasks should be started can be easier than having to know the tasks IDs.
This both makes it easier for 3rd party systems to start Qlik Sense tasks and easier for Sense admins to manage which tasks should be startable by 3rd party systems.
Trigger full/partial app reloads from load script or upstream systems
Sometimes you just want to reload an app without also having to create a reload task.
When it comes to partial app reloads it’s not even possible to do these from a Sense reload task.
Butler’s API makes prvovides a solution: Just pass in an app ID to reload together with task IDs of the tasks that should be started when the app is done reloading (different tasks can be started depending on app reload success or failure).
The partial reload feature is of special interest as it can be used to trigger faster incremental execution of of Extract-Transform reload chains. Great for keeping data in Sense apps updated during the course of a day!
Flexible scheduling of app reloads in Qlik Sense Enterprise on Windows
Using the scheduler built into Qlik Sense you can’t for example create schedules that are limited to a parts of a day.
This is a pretty common scenario though - you want to reload an app hourly from say 3 am to 3 pm.
You can set this up in Sense, but it involves creating a lot of triggers for the reload task, which becomes a nightmare to maintain.
Butler’s task scheduler is built on Cron, which has been used in Linux for decades. Battle proven and very flexible.
Passing parameters between reload tasks
This has always been hard both in QlikView and Sense.
Butler’s key-value store makes it much easier to pass values from one app to the next in a reload chain.
Storing state across several apps
The key-value store can also be used to keep state in general across several apps or parts of a Qlik Sense environment.
Maybe a Development cluster needs to share information in real time with the Test and Production clusters?
Easily solved using Butler’s key-value store.
A demo showing parameter passing between apps is found here.
Time-to-live (TTL) for key-value pairs
Key-value pairs can optionally be set up with a time-to-live (ttl) parameter. If ttl is set, the KV pair will auto-delete when the ttl expires.
Make new data reach end users as quickly as possible
See above. Have the upstream data source initiate Sense app reloads, either via Butler’s REST API or via MQTT messages sent to Butler.
Using MQTT to notify downstream systems that Sense is done processing data
Use Butler’s API endpoints for MQTT handling to send and receive MQTT publish-subscribe messages.
MQTT (and the pubsub concept in general) is a great way for systems to communicate reliably with each other.
A demo app is available, showing how MQTT messages can be sent from Sense load scripts. More info here.
Create directories, copy/move/delete files
In “standard mode” apps reloading in Qlik Sense Enterprise on Windows can’t access the file system of the Sense servers. This is a good thing because it adds a lot of security.
From time to time you need to delete temp QVDs though, or copy or move data files from one directory to another.
Butler has REST API endpoints for these use cases, but as those endpoints are locked down to only work on specific, configurable directiories they don’t result in the same security issues as seen in for example QlikView or Sense running in legacy mode.
Exporting apps as JSON can be very useful for backup purposes. Doing regular snapshots of all apps in a Sense cluster is a fast and space-effective way of keeping point-in-time backups.
Slack messages can include full formatting (web links, text formatting etc), as well as “poking” users.
I.e. notifying specific Slack users that they have a new message.
Can for example be used to notify user(s) that an app has reloaded with new data, or that some error condition has occured.
If Butler is running on Windows (server or even desktop) it can monitor one or more Windows services.
This feature is not available when running Butler on Linux, macOS or in Docker.
Monitoring here means tracking the services’ state and sending messages to email, InfluxDB, New Relic, MQTT, Webhooks, Slack or Teams when services stop or start.
It can for example be used to get alerts if a Qlik Sense service for some reason stops.
The concept is not limited to Qlik Sense services though - any Windows service can be monitored.
Monitor and release Qlik Sense user licenses
Butler can monitor the usage of Qlik Sense user licenses and store the data in InfluxDB, from where the license data can be visualized in Grafana.
This makes it easy to track (and alert if needed) on the number of used licenses, how many are available and when it’s time to get more licenses.
Butler can also automatically release Professional and Analyzer user licenses that have been inactive for a certain period of time.
This is useful in environments where some users use Sense sporadically, for example only during certain times of the year.
In such cases it’s a waste of resources to keep the license assigned to the user when it’s not being used.
Things that Butler used to do, but don’t really care for any more…
Some features age with grace, others don’t.
Here’s a list of features that are candidates for removal from Butler, or that have already been removed.
Candidates for removal in coming versions
Real-time metrics around active users
While a good idea in theory, Butler just wasn’t the vehicle for this.
The way Butler approached this was to have Sense’s log4net logging framework send UDP messages to Butler when users logged in/out or sessions started/ended. This certainly works (quite well in fact!), but it also has inherent issues.
For example, when Butler was started it wouldn’t capture currently active users or sessions - it was only after some event captured in the logs that Butler would update it’s internal counters. This meant that it would take some time (sometimes quite long) until the metrics were even approaching the real number of users using Sense.
There was also the risk of Butler missing UDP messages and not registering the associated log event.
The shortcomings above lead to the creation of the Butler SOS project, which has over the years evolved into a very comprehensive open source tool for professional grade, real-time monitoring of Qlik Sense.
Please meet the Butlers. They’re a nice, wild bunch!
Butler started out with a very specific need to start Sense reloads from outside systems.
Back in those days the cloud wasn’t a thing, and Sense was only available on Windows servers.
Over the years a few projects (for example Butler SOS, which simplifies day 2 operations of client-managed Sense ([1], [2]) have spun off from the original Butler project, and still other projects have been created from scratch to solve specific challenges around developing Sense apps and running Qlik Sense server environments or developing apps for Qlik Sense Cloud.
Real-time operational metrics for Qlik Sense. A must-have if you are responsible for a Sense environment with more than a dozen or so users.
Simplifies day 2 operations of client-managed Sense.
Butler SOS makes it possible to detect and alert on issues as they happen, rather than in retrospect much later.
Several storage and visualisation options available, including InfluxDB + Grafana, and New Relic.
Automates the creation of sheet icons for both Qlik Sense Cloud and client-managed Qlik Sense Enterprise on Windows (QSEoW) applications.
It’s a cross platform command line tool which given the correct Sense credentials will take screen shots of all sheets in a Sense app (or all apps on a Sense server!), then create thumbnail versions of those screenshots.
Finally those thumbnails will be set as sheet icons.
No more manual screenshot taking, resizing images, navigating hundreds of sheets in dozens of apps.
Start Butler Sheet Icons instead and go get a nice fika.
The tool can be used stand-along or as part of an automated release process.
Given the name of this tool it doesn’t sound like a member of the Butler family.
Let’s say Ctrl-Q is a sibling of the Butler bunch.
While the Butler tools are (usually) intended to solve and simplify rather specific use cases, Ctrl-Q is aimed at being the lazy Qlik developer’s best friend.
Let’s say there is some manual, tedious, time consuming and error prone activity that a Qlik Sense developer is faced with.
For example importing dozens of apps from QVF files and creating a hundred associated reload tasks.
Ctrl-Q lets you do this with a single command, using definitions in an Excel file. Instead of spending a day on this the actual execution takes a minute or so.
In other words: Ctrl-Q focus on high-value use cases that are difficult or impossible to solve using other tools.
The following tools are no longer actively maintained, but they are still available on GitHub.
If you find them useful, feel free to use them.
Butler CW
Butler Cache Warmer. Cache warming is the process of proactively forcing Sense apps to be loaded into RAM, so they are readily available when users open them.
Using Butler CW is an easy way to make your end users’ experience of Sense a little better.
Now that client-managed Qlik Sense has its own cache warming feature (as of 1st half 2024), Butler CW will eventually be phased out.
This tool is mainly of interest if you have lots of QVDs and apps, but when that’s the case it’s of paramount importance to understand what apps use which QVDs. In other words what data lineage looks like.
Butler Spyglass also extracts full load scripts for all Sense apps, creating a historical record of all load scripts for all Sense apps.
This tool makes it easy to tap into the Qlik Sense notification API. From there you can get all kinds of notifications, including task reload failures and changes in session state (user login/logout etc).
Visual looks is important when it comes to analytics, and this holds true also for Sense apps.
The Butler Icon Uploader makes it easy to upload icon libraries (for example Font Awesome) to Qlik Sense Enterprise. With such icons available it is then easy for app developers to use professional quality sheet and app icons in their Sense apps.
Features are added, bugs fixed. How are Butler versions set?
In the spirit of not copying information to several places, the version history is kept as annotations of each release on the GitHub release page.
Version numbers include up to 3 levels, for example version 4.6.2 (which is a fictitious version):
4 is the major version. It is increased when Butler has added major new features, or in other ways changed in major ways. If following this principle, breaking changes should always result in a bumped major version.
6 is the minor version. This indicates a smaller update, when one or a few minor features have been added.
2 is the patch level. When individual bugs are fixed, these are released with an increased patch level.
Note 1: Major and minor updates usually include bug fixes too.
Note 2: If a version of 5.2 is mentioned, this implicitly means 5.2.0.
Documentation updates
Starting with Butler version 4.0, this documentation site will offer both latest and earlier site versions.
Select which doc site to view in drop-down list in the upper right corner of the page.
1.5 - Contribution guidelines
How to contribute to Butler.
Butler is an open source project, using the MIT license.
This means that all source code, documentation etc is available as-is, at no cost.
It however also means that anyone interested can - and is encouraged to - contribute to the project!
Butler is developed in Node.js, with support from various NPM modules.
We use Hugo to format and generate this documentation site, the Docsy theme for styling and site structure.
Hugo is an open-source static site generator that provides us with templates, content organisation in a standard directory structure, and a website generation engine. You write the pages in Markdown (or HTML if you want), and Hugo wraps them up into a website.
All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult GitHub Help for more information on using pull requests.
Creating an issue
If you’ve found a problem - or have a feature suggestion - with Butler itself or the documentation, but you’re not sure how to fix it yourself, please create an issue in the Butler repo. You can also create an issue about a specific doc page by clicking the Create Issue button in the top right hand corner of the page.
Security/Disclosure
If you discover a serious bug with Butler that may pose a security problem, please disclose it confidentially to security@ptarmiganlabs.com first, so that it can be assessed and hopefully fixed prior to being exploited. Please do not raise GitHub issues for security-related doubts or problems.
1.6 - Telemetry
What’s telemetry and why is it important?
Sharing telemetry data from Butler is optional.
You can use all Butler features without sharing telemetry data.
That said, if you find the Butler tool useful you are strongly encouraged to leave Butler’s telemetry feature turned on.
Having access to this data greatly helps the Butler developers when they design new features, fix bugs etc.
The Butler developers care about you - sharing telemetry data is your way of showing you care about them.
Telemetry is the in situ collection of measurements or other data at remote points and their automatic transmission to receiving equipment (telecommunication) for monitoring.
In the context of software tools (including Butler) telemetry is often used to describe the process of sending information about the tool itself to some monitoring system.
Why telemetry in Butler
That is a very good question.
For many years there was no telemetry at all in Butler.
Development of new features were driven mainly by what features were needed at the time.
Or the fact that Qlik released some new feature in Sense and Butler was a way to test that new feature from the perspective of the Sense APIs.
That’s all good but today Butler is a rather significant tool with features spanning quite different areas (alerting, task scheduling, key-value store and more).
This multitude of features is also one of the core reasons for adding telemetry to Butler:
Which Butler APIs and features are actually used out there?
Which operating systems, Node.js versions and hardware platforms is Butler running on?
Without this information the Butler developers will keep working in the dark, not really knowing where to focus their efforts.
On the other hand - with access to telemetry data a lot of possibilities open up for the Butler developers:
If telemetry shows that no one uses a particular feature, maybe that feature should be scheduled for deprecation?
The opposite of the previous: If lots of users use a specific Butler feature, then that feature is a candidate for future focus and development.
Telemetry will show if lots of users run Butler on old Node.js versions. Knowing this its possible to set a migration schedule for what Node.js versions are supported - avoiding hard errors when some old Node.js version is no longer supported by Butler.
Same thing for understanding what operating systems Butler runs (and should be supported) on.
Information about what API endpoints are enabled in the Butler config file. Why: This tells the Butler developers which features are used and which aren’t. This is critical information when it comes to planning where to focus future development efforts.
Information about what features are enabled and which are disabled. Why: Same as above. Knowing which features are used (and are thus important) allows the Butler developers to better plan future work.
Information about Butler’s execution environment (operating system, hardware architecture, Node.js version etc). Why: Ideally the Butler developers want to use as modern versions of Node.js as possible. But if telemetry shows that lots os Butler instances use old Node.js versions or run on some (yet) untested/unverified Linux version - then maybe those older Node.js/Linux versions must be supported for yet some time.
What's not shared
The telemetry data will never include:
Data that can identify your Sense environment or the server on which Butler runs. This includes IP/MAC addresses or other network information, server names, Docker container metadata or similar.
Any actual data sent via Butler APIs.
Qlik Sense or other certificates or credentials in any shape or form.
Where is telemetry data sent
The telemetry data is sent to the PostHog service, using their database in the European Union.
Deleting telemetry data
Even though no-one (not even the Butler developers or Ptarmigan Labs who manage the telemetry database!) has any way of ever connecting the data sent by your Butler instance to you (it’s all anonymized, remember?), there can be cases where telemetry data must be deleted.
It’s purpose is to uniquely identify the Butler instance - nothing else.
If Butler is stopped and started agagin the same ID will be used.
Some sensitive information is used to create the ID, but as the ID is anonymized using a one-way hash function before sent as part of the telemetry data, no sensitive information leaves your servers.
The ID field is created as follows:
Combine the following information to a single string
MAC address of default network interface
IPv4 address of default network interface
IP or FQDN of Sense server where repository service is running
System unique ID as reported by the OS. Not all OSs support this though, which is why field 1-3 above are also needed to get a unique ID.
Run the created string through a one-way hashing/message digest function.
Butler uses Node.js’ own Crypto library to create a SHA-256 hash, using the default network interface’s MAC address as salt.
Security is increased due to the fact that the salt never leaves the server where Butler is running.
The bottom line is that it’s impossible to reverse the process and get your the IP, host name etc used in step 1 above.
Then again - this is cryptography and things change.
But if you trust the certificates securing Sense itself, then the ID anonymization used by Butler should be ok too.
The result is a string that uniquely identifies the Butler instance at hand, without giving away any sensitive data about the system where Butler is running.
See below for an example of what the id field looks like.
The id field is shown during Butler startup as “Instance ID”.
What data is included in the telemetry messages?
See above for details.
In general the telemetry includes information about which Butler API endpoints and features are enabled vs disabled.
A unique, anonymized ID is included too, it’s unique to each Butler instance and is used soley to distinguish between different Butler instances.
Finally some information about Butler’s execution environment is included. Things like operating system, Node.js version used etc.
Can my Sense environment be identified via telemetry data?
Short answer: No.
Longer answer: No information about your Sense environment is sent as part of telemetry. No IP addresses or server names, no IDs of Sense apps/tasks/etc, no information about what actual data passed through Butler’s APIs, or any other data that can be linked to your Sense environment is included in the telemetry data.
2 - Getting started
What you need to know to get Butler off the ground.
2.1 - Overview
Butler makes it both easier to develop Qlik Sense apps and run client-managed Qlik Sense clusters.
This page gives you the general steps to get started with Butler.
It also explains how Butler relates to and uses other tools and services.
Getting started: 1-2-3
The main components of Butler are outlined in the system diagram above.
Remember!
Individual parts of Butler can be enabled/disabled in the main config file.
If you’re unsure what each REST API endpoint does, the API docs is the place to check.
1. Installation
Follow the installation instructions - they will guide through the setup process, including requirements and customisation.
Feel free to browse through the concepts and examples to get an understanding of how to use Butler.
2.2 - Install
How to install Butler, including requirements and on what platforms Butler can be installed.
Warning
Butler was developed with InfluxDB version 1.x in mind.
If you intend to use Butler together with InfluxDB you need to be aware of the following:
InfluxDB is currently available in version 2.x and while this version brings lots of new goodies, it’s not out-of-the-box compatible with Butler.
For that reason you should use the latest 1.x version of InfluxDB, which at the time of this writing is 1.8.4.
If you do not intend to use any InfluxDB related features of Butler you can simply disregard this warning.
In due time Butler will be updated to support InfluxDB 2.x too.
Given the cross platform nature of Node.js (which is the language Butler is written in), Butler can run on lots of different hardware platforms and operating systems.
It is therefore difficult to give detailed installation instructions for each possible installation scenario. This site thus tries explain how to get started with Butler in some of the most common scenarios.
Pre-built binaries are available for Windows, macOS and Linux. When using these there is no need to install Node.js, as the Node.js runtime is bundled into the binaries.
Using these binaries is the easiest - and thus recommended - way of using Butler.
…unless you want to use Docker, which is also a great option.
Getting started
Sorry - there is no installer for Butler.
The pre-built binaries for Windows, macOS, Linux and Docker simply work as-is when combined with a properly set up configuration file.
If you still want to run Butler as Node.js app you will first need to install Node.js.
The instructions on the pages below should provide good guidance, if you still run into troubles you can always reach out via the GitHub discussion forums.
What’s required to use Butler
A Butler executable for your operating system
A Butler config file adapted to your specific Qlik Sense environment
A way to authenticate with Qlik Sense APIs
Certificates for Qlik Sense Enterprise on Windows
JSON Web Token (JWT) for Qlik Sense Cloud
What
Comment
Qlik Sense Enterprise on Windows
Most Butler features target client-managed Qlik Sense Enterprise on Windows (QSEoW).
Qlik Sense Cloud
Some features are available for Qlik Sense Cloud, for example the ability to send app reload failed alerts to email, Slack, Teams or script log on local disk.
Butler executable
Mandatory. A Butler executable of some kind. This would be a) a stand-alone binary for the operating system you plan to use, b) a Docker image from which a Butler container can be created or c) the Butler source code plus Node.js installed.
MQTT broker
Optional. MQTT is used for both in- and out-bound pub-sub messaging. Butler assumes a working MQTT broker is available, the IP of which is defined in the Butler config file. Mosquitto is a great open source broker. It requires very little hardware to run, even the smallest (usually free) Amazon/Google/Microsoft/… instance is enough, if you want a dedicated MQTT server. If you don’t care about the pubsub features of Butler, you don’t need a MQTT broker. In this case you can disable the MQTT features in the config YAML file.
Optional. A commercial online service offering a vast set of observability features of which Butler uses just a few. Reload failure alerts are for example very nicely handled in New Relic as you get access to the script logs (similar to what can be done with InfluxDB + Grafana) right in the New Relic UI. New Relic’s free tier usually goes a long way towards the need of SenseOps and Butler use cases, so it’s easy to try out New Relic.
Optional. A smaller but very nice, mobible-first incident management service. Using Signl4 it’s easy to get failed reload alerts to your phone. The service also makes it easy to set up on-call schedules, escalate incidents if needed etc.
2.2.1 - Decide how to run Butler
On what platforms does Butler run?
The short answer is: Almost anywhere.
The pre-built binaries for Windows, macOS, Linux and Docker should cater for most use cases.
If you have some other, more exotic platform or operating system you want to run Butler on that’s probably possible too.
Butler is built on Node.js and as long as Node.js is available on the platform/operating system of your choice there is a good chance Butler will run there.
Butler has been successfully used on Windows Server, Windows 10, various Linux distributions, in Docker, Kubernetes, on Mac OS and even on Raspberry Pis. And a Raspberry Pi based Kubernetes cluster.
Your platform options thus typically fall into three categories:
Butler as a stand-alone executable
Here you will be using the pre-built Butler binaries (Windows, Linux, Mac OS) that are available for Butler 7.2 and later.
When using third party tools these binaries can be started as services.
For example, on Windows the free NSSM tool is a great way to run Butler as a Windows service.
Another good tool is PM2 which works well on Linux-ish platforms.
If you have access to or can set up a container runtime environment, this is a great way to running Butler.
Installation is less error prone compared to installing Butler as a native Node.js app, you get all the benefits from the Docker ecosystem (monitoring of running containers etc), and upgrades to future Butler versions become trivial.
If you have access to a Kubernetes cluster, that is usually an even better option than Docker. Kubernetes can be daunting when first approached, but will give you superb reliability, failover and restarts if a server goes down or becomes unresponsive etc. All major cloud providers (Microsoft Azure, Google, Amazon etc) offer Kubernetes services.
Rancher’s K3s is a very good way to get started with self hosted Kubnernetes. Fully featured, well supported and a vibrant developer community.
Butler as a Node.js application
This option means you will first install Node.js on your server of choice, then Butler and it’s dependencies.
It works perfectly well but is the most demanding when it comes to amount of work needed to get started.
2.2.2 - Running Butler as a native, pre-built application
How to install the pre-built, stand alone Butler applications.
Downloading the app
Download Butler for your preferred operating systym.
Decide where to install Butler
It is usually a good starting point to run Butler on the Sense server. If there are more than one server in the Sense cluster, Butler can be placed on the reload server (as the /createDir endpoint then can be used to create folders in which QVD and other files can be stored).
On the other hand, you might want to keep the Sense servers as clean as possible (with respect to software running on them). If that is a priority you should install Butler on some other server.
The bottom line is that Butler can run on any server, as long as there is network connectivity to the Sense server(s).
It’s usually a good idea to keep 3rd party tools installed in the same directory tree, to maintenance as easy as possible.
A good place for Butler could be c:\tools\butler or d:\tools\butler on Windows, for example.
Download Butler
Download the latest version from the releases page.
Make sure to get the binary file for your preferred operating system.
Unzip the downloaded file, then copy or move the butler binary to the desired directory (e.g. c:\tools\butler) and that’s it.
Tip
On Windows you must “unblock” the ZIP file before extracting the Butler binary from it.
This is basically a way to tell Windows that the ZIP is safe even though it was downloaded from Internet.
Right click on the ZIP file, then select Properties.
If there is an “Unblock” check box in the lower right part of the properties window you should click that box and hit OK.
Then unpack the ZIP file.
The macOS version of Butler is signed using Apple’s official app signing process.
This means you may see a warning the first time you start Butler, but after that there should be no more warnings.
2.2.3 - Running Butler in Docker
How to install Butler as a Docker container.
Installation steps
The following steps give some guidance on how to get Butler running on Docker.
Here Mac OS was used, things will look different on Linux and Windows.
Note: While the console logs below refer to an older version of Butler’s Docker image, the steps involved are the same also for current/most recent version of Butler.
proton:~ goran$ mkdir /Users/goran/butler
proton:~ goran$ cd /Users/goran/butler
proton:butler goran$ mkdir -p config/certificate
proton:butler goran$
proton:butler goran$ wget https://raw.githubusercontent.com/ptarmiganlabs/butler/master/src/docker-compose.yaml
--2021-10-25 17:07:23-- https://raw.githubusercontent.com/ptarmiganlabs/butler/master/src/docker-compose.yaml
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 660[text/plain]Saving to: βdocker-compose.yamlβ
docker-compose.yaml 100%[=====================================================================================================================================>]660 --.-KB/s in 0s
2021-10-25 17:07:23 (42.0 MB/s) - βdocker-compose.yamlβ saved [660/660]proton:butler goran$ cat docker-compose.yaml
# docker-compose.ymlversion: '3.3'services:
butler:
image: ptarmiganlabs/butler:6.1.0
container_name: butler
restart: always
ports:
- "8080:8080"# REST API available on port 8180 to services outside the container - "9998:9998/udp"# UDP port for task failure events volumes:
# Make config file accessible outside of container - "./config:/nodeapp/config" - "./log:/nodeapp/log" environment:
- "NODE_ENV=production" logging:
driver: json-file
options:
max-file: "5" max-size: "5m"proton:butler goran$
At this point you should
Export certificates from the Qlik Sense QMC. Export a full set of certificates in PEM format, no psasword on the certificates.
Copy the certificates to the ./config/certificate directory.
Copy the template config file from the GitHub repository to the ./config directory, modify it as needed based on your system(s) and which Butler features you want enabled, and rename it to for example production.yaml.
You can name the config file anything, but its name has to match the NODE_ENV environment variable, as set it the docker-compose.yaml file.
Optional. Copy the template schedule file to the location specified in Butler’s config file. This is only needed if you manually want to add schedules. If using the API to create schedules, there is no need to first manually create a schedules file (the schedule file will be created by Butler in this case).
In the terminal where you ran docker-compose, you will see a new line saying that a app list was retrieved:
butler | 2021-10-25T19:20:50.356Z info: /v4/senselistapps called from 192.168.176.1
2.2.4 - Running Butler as a Node.js application
How to install Butler as a Node.js application.
Selecting an OS
While Qlik Sense Enterprise is a Windows only system, Butler should be able to run on any OS where Node.js is available.
Butler has been succesfully used - during development and production - on Windows, Linux (Debian and Ubuntu tested) and mac OS.
Installation steps
The steps below outline the steps needed to install Butler as a native Node.js application on for example Windows Server.
Install node.js
Butler has been developed and tested using the 64 bit version of Node.js. The most recent LTS (Long Term Support) version is usually a good choice.
Decide where to install Butler
It is usually a good starting point to run Butler on the Sense server. If there are more than one server in the Sense cluster, Butler can be placed on the reload server (as the /createDir endpoint then can be used to create folders in which QVD and other files can be stored).
On the other hand, you might want to keep the Sense servers as clean as possible (with respect to software running on them). If that is a priority you should install Butler on some other server.
The bottom line is that Butler can run on any server, as long as there is network connectivity to the Sense server(s).
Download Butler
Download the repository zip from the releases page.
Do not just clone the Butler repository as that will give you the latest development version, which may not yet be fully tested and packaged.
The exception is of course if you want to contribute to Butler development - then forking and cloning the repository is the right thing to do.
Install node dependencies
From a Windows command prompt (assuming the Butler ZIP file/repository was saved to d:\node\butler):
d:
cd \node\butler\src
npm install
This will download and install all Node.js modules used by Butler.
On some OSs you’ll get some warnings during the installation - they are usually harmless. Try to run Butler even if you got some warnings, chances are good that things will work just fine. This is common on especially Windows Server and is a result of some Butler dependencies being primarily developed on Linux rather than Windows.
2.3 - Setup
Everything you wanted to know about Butler configuration but never dared to ask.
Butler can use multiple config files. Here you learn to control which one is used by Butler.
A description of the config file format is available here.
Select which config file to use
Butler uses configuration files in YAML format.
A default config file called production_template.yaml is included in the release Zip files on the download page. It is also available in the GitHub repository.
Make a copy of it, then rename the copy default.yaml, production.yaml, staging.yaml or something else suitable to your specific use case.
Update it as needed (see the config file reference page for details).
Trying to run Butler with the default config file (the one on GitHub) will not work - you must adapt it to your server environment. For example, you need to enter the IP or host name of you Sense server(s), the IP or host name where Butler is running etc.
All config entries are mandatory
As of Butler 9.0 the config file’s structure will be validated when Butler starts. If there are any errors (missing entries etc) in the config file, Butler will not start.
This means that all config file entries are mandatory. If some feature is not use the corresponding entry can be left empty.
Finally, Butler must somehow be given instructions about where to look for the config file.
This can be done in several ways depending on how Butler is used, see below.
Config file for stand-alone Butler
Let’s run Butler on a Windows Server using PowerShell, without any options or parameters:
There is an option --configfile (or its short version -c) that let us control which config file to use.
In this example the config file .\config\butler-config.yaml is used.
Let’s try again with the -c option:
Butler now starts nicely using the specified config file.
Tip
When using the standalone Butler executables you can use an absolute or a relative path when specifying the location of the config file.
For example, c:\tools\butler\config\butler-config.yaml is an absolute path, while .\config\butler-config.yaml would be a relative path.
Config file when running Butler as a Node.js app
When running Butler as a Node.js app, i.e. starting it with node butler.js, Butler will look for a config file in the ./config subdirectory.
The name of the config file matters.
Butler looks for an environment variable called βNODE_ENVβ and then tries to load a config file named with the value found in NODE_ENV.
Example: NODE_ENV=production
Butler will look for a config file config/production.yaml.
Config file when running Butler in a Docker container
The template docker-compose.yaml file in the GitHub repository shows how to specify which config file that will be used:
# docker-compose.ymlservices:butler:image:ptarmiganlabs/butler:latestcontainer_name:butlerrestart:alwaysports:- "8080:8080"# REST API available on port 8180 to services outside the container- "9998:9998/udp"# UDP port for task failure eventsvolumes:# Make config file accessible outside of container- "./config:/nodeapp/config"- "./log:/nodeapp/log"environment:- "NODE_ENV=production"logging:driver:json-fileoptions:max-file:"5"max-size:"5m"
Here the environment variable NODE_ENV is set to “production”, and the host OS’ ./config directory is mapped to the container’s /nodeapp/config directory.
As there is no --configfile command line option present the default setting will be used, which is to look for the config file in the config directory right under the directory where the docker-compose.yaml file is located.
The file name is determined by Butler (running in the container) looking at the NODE_ENV env variable.
Bottom line is that the ./config/production.yaml (relative to the location of docker-compose.yaml) file will be used.
Running several Butler instances in parallel
If you have several Sense clusters (for example DEV, TEST and PROD environments) you may want to run several Butler instances.
The solution is to create several config files: butler_dev.yaml, butler_test.yaml and butler_prod.yaml.
In this scenario three instances of Butler should be started, each given a different config file via the --configfile command line option.
Note: If running several Butler instances in parallel, you must also ensure that each one uses unique port numbers for their respective REST APIs, UDP servers etc.
Setting environment variables
The method for setting environment variables varies between operating systems:
On Windows: set NODE_ENV=production
Mac OS or Linux: export NODE_ENV=production
If using Docker, the NODE_ENV environment varible is set in the docker-compose.yml file (as already done in the template docker-compose file.)
2.3.2 - Minimal configuration to start Butler
The provided sample config file is a good starting point for your own config file.
It contains the minimum settings needed to start Butler, but a few settings in it must be updated to match your environment.
Starting Butler with a minimal config file
Configuring Butler via its YAML format config file is probably the most difficult part of setting up Butler.
It’s however also the only way to configure Butler, so it needs to be done.
To make that process easier, a minimal config file called production_template.yaml is included in the release Zip files on the download page.
Note
The included sample config file contains the minimum settings needed to start Butler, but a few settings in it must be updated to match your environment.
These are described in the comments at the beginning of the config file.
The settings are mostly related to the host names and ports of the Qlik Sense server(s) you want Butler to connect to, and the host name and port of the machine where Butler is running.
After working through the instructions in the config file, you should be able to start Butler with the following command (PowerShell in this case):
Most Butler features are disabled in the minimal config file, but it’s a good starting point for your own config file.
To summarize, the recommended steps to get Butler up and running are:
Download the latest Butler release from the download page. Precompiled binaries are available for Windows, Linux, macOS and Docker (on Docker Hub).
Copy the production_template.yaml config file (which is included in the Zip file) to a new file, e.g. butler-config.yaml.
Add the needed settings to butler-config.yaml as described in the comments at the beginning of that that file.
Start Butler, passing in the path to the config file as the --configfile (or -c) parameter.
Once Butler is running in this minimal configuration, you can start enabling more features in the config file, for example failed task monitoring, monitoring of Windows services, Sense licenses and much more.
Example: Things to change in the minimal config file
The following is an example of the comments at the beginning of the production_template.yaml config file, describing what needs to be changed in it to start Butler with a minimal configuration.
The example below is for Butler 12.4.0, but the same principle applies to later versions too.
---Butler:# General notes: # - File and directory paths in this sample config file use Linux/Mac syntax, i.e. using forward slashes.# Windows paths work just as well, just make sure to quote them with single or double quotes.# - All entries in the config file are mandatory in the sense that they must be present.# However, if a feature is not used the corresponding config entries can contain # any value (for example the provided default ones).# - Butler will start using the settings in this file if the follwing settings are set first:# - Butler.cert.clientCert: Set to the path of the client certificate file. If relative paths cause issues, use an absolute path.# - Butler.cert.clientCertKey: Set to the path of the client key file. If relative paths cause issues, use an absolute path.# - Butler.cert.clientCertCA: Set to the path of the CA certificate file. If relative paths cause issues, use an absolute path.# - Butler.configEngine.host: Set to the IP or FQDN of the host where the Sense engine service is running.# - Butler.configEngine.port: Set to the port where the Sense engine service is listening.# - Butler.configQRS.host: Set to the IP or FQDN of the host where the Qlik Repository Service (QRS) is running.# - Butler.configQRS.port: Set to the port where the Qlik Repository Service (QRS) is listening.# - Having set the above settings, Butler will start and run, but it will not do anything useful until you configure# the various monitoring and notification settings, as described at https://butler.ptarmiganlabs.com.......
2.3.3 - Connecting to a Qlik Sense server
Details on how to configure the connection from Butler to Qlik Sense Enterprise on Windows.
What’s this?
In order to interact with a Qlik Sense Enterprise on Windows (QSEoW) environment, Butler needs to know a few things about that environment. This is true no matter if the Sense cluster consists of a single Sense server or many.
Settings in config file
---Butler:......# Certificates to use when connecting to Sense. Get these from the Certificate Export in QMC.cert:clientCert:<path/to/cert/client.pem>clientCertKey:<path/to/cert/client_key.pem>clientCertCA:<path/to/cert/root.pem># If running Butler in a Docker container, the cert paths MUST be the following# clientCert: /nodeapp/config/certificate/client.pem# clientCertKey: /nodeapp/config/certificate/client_key.pem# clientCertCA: /nodeapp/config/certificate/root.pemconfigEngine:# engineVersion: 12.170.2 # Qlik Associative Engine version to use with Enigma.js. Ver 12.170.2 works with Feb 2019engineVersion:12.612.0# Qlik Associative Engine version to use with Enigma.js. Works with Feb 2020 and othershost:<FQDN or IP of Sense server where Sense Engine is running>port:<Port to connect to, usually 4747>useSSL:trueheaders:static:# http headers that are sent with every request to QRS. The "X-Qlik-User" is mandatory.- name:X-Qlik-User # Header used to identify what user connection to QRS is made asvalue:UserDirectory=Internal;UserId=sa_repository # What user connection to QRS is made as rejectUnauthorized:falseconfigQRS:authentication:certificateshost:<FQDN or IP of Sense server where QRS is running>useSSL:trueport:4242headers:static:# http headers that are sent with every request to QRS. The "X-Qlik-User" is mandatory.- name:X-Qlik-User # Header used to identify what user connection to QRS is made asvalue:UserDirectory=Internal;UserId=sa_repository # What user connection to QRS is made as rejectUnauthorized:false# Set to false to ignore warnings/errors caused by Qlik Sense's self-signed certificates.# Set to true if the Qlik Sense root CA is available on the computer where Butler SOS is running.......
2.3.4 - Connecting to Qlik Cloud
Details on how to configure the connection from Butler to Qlik Sense Cloud.
What’s this?
In order to interact with a Qlik Sense Cloud environment, Butler needs to know a few things about that environment.
How Butler gets events from Qlik Sense Cloud
A few things to note about how Butler gets events from Qlik Sense Cloud:
When an app reload fails in Qlik Sense Cloud, an outgoing webhook is triggered.
This webhook calls a https endpoint somewhere on the Internet, for example a serverless function in Azure or AWS (or on-prem).
The function forwards the event as an MQTT message to a MQTT broker, on a well-defined topic.
Butler listens to this topic and reacts to the event.
This model may seem a bit complex, but it has a few advantages:
It is scalable. The serverless function can be scaled up and down as needed. MQTT brokers also scale well.
It is flexible. The serverless function can be written in any language, and can be hosted anywhere.
It is secure. The serverless function can be locked down to only accept incoming webhooks from Qlik Sense Cloud. The option would be to expose Butler directly to the Internet, which is not recommended.
The effect is an asynchronous, scalable and secure way of getting events from Qlik Sense Cloud to Butler.
Settings in config file
Butler:......qlikSenseCloud:# Settings for Qlik Sense Cloud integrationenable:falseevent:mqtt:# Which QS Cloud tenant should Butler receive events from, in the form of MQTT messages?tenant:id:tenant.region.qlikcloud.comtenantUrl:https://tenant.region.qlikcloud.comauthType:jwt # Authentication type used to connect to the tenant. Valid options are "jwt" auth:jwt:token:<JWT token> # JWT token used to authenticate Butler when connecting to the tenant# Qlik Sense Cloud related links used in notification messagesqlikSenseUrls:qmc:<URL to QMC in Qlik Sense Cloud>hub:<URL to Hub in Qlik Sense Cloud>comment:This is a comment describing the tenant and its settings# Informational only......
2.3.5 - Configuring Butler's REST API
Butler’s REST API can be enabled/disabled in itself. If the API is enabled, individual API endpoints can then be enabled/disabled as needed.
By only enabling the endpoints needed for your Qlik Sense environment, memory usage is minimised and security maximised.
What’s this?
Butler offers a set of REST API endpoints. While these endpoints are tested for stability and correct functionality as part of each release, it’s always good practice to only enable the endpoints really needed.
Thus, individual endpoints of Butler’s API can be turned on or off in the main config file.
Configuring the REST API
Butler:......restServerConfig:enable:false# Should Butler's REST API be started? Must be true if *any* API endpoints are to be used.serverHost:<FQDN or IP (or localhost) of server where Butler is running> # Use 0.0.0.0 to listen on all network interfaces (e.g. when running in Docker!).serverPort:8080# Port where Butler's REST is available. Any free port on the server where Butler is running can bse used.backgroundServerPort:8081
Ports used by Butler
Butler exposes its REST API on a TCP port defined in the Butler.restServerConfig.serverPort setting in the config file.
Similarly, the host name Butler listens at is defined by the Butler.restServerConfig.serverHost setting. This would typically be the IP number, host name or fully qualified domain name of the computer where Butler is running.
Note that Butler uses two ports for its REST API: One external facing port and one used internally. Both must be dedicated to Butler on the computer where Butler is running.
Using two ports (one external facing and one internal) is not ideal, but it was an easy yet stable way of solving some technical challenges around Butler’s use of the X-HTTP-Method-OverrideHTTP header.
Just make sure that the two settings Butler.restServerConfig.serverPort and Butler.restServerConfig.backgroundServerPort aren’t the same and aren’t already in use, and all should be fine.
Rate limiting the REST API
Butler’s REST API can be rate limited to prevent abuse.
Rate limiting is configured by the --api-rate-limit command line parameter when starting Butler.
The parameter takes a single integer value, which is the number of API calls allowed per minute.
Set to 0 to disable rate limiting.
Enabling individual API endpoints
Each enabled endpoint will result in Butler using more memory and CPU. Thus only enable the endpoints that are needed.
Endpoint specific settings
In some cases some extra configuration is needed to make an API endpoint function properly.
This information is configured in the Butler.restServerEndpointsConfig section in the config file.
Settings in config file
---Butler:......# Enable/disable individual REST API endpoints. Set config item below to true to enable that endpoint.restServerEndpointsEnable:apiListEnbledEndpoints:falsebase62ToBase16:falsebase16ToBase62:falsebutlerping:falsecreateDir:falsecreateDirQVD:falsefileDelete:falsefileMove:falsefileCopy:falsekeyValueStore:falsemqttPublishMessage:falsenewRelic:postNewRelicMetric:falsepostNewRelicEvent:falsescheduler:createNewSchedule:falsegetSchedule:falsegetScheduleStatusAll:falseupdateSchedule:falsedeleteSchedule:falsestartSchedule:falsestopSchedule:falsesenseAppReload:falsesenseAppDump:falsesenseListApps:falsesenseStartTask:falseslackPostMessage:falserestServerEndpointsConfig:newRelic:postNewRelicMetric:# Setings used by post metric to New Relic API endpointdestinationAccount:- First NR account- Second NR account# As of this writing the valid options are# https://insights-collector.eu01.nr-data.net/metric/v1# https://insights-collector.newrelic.com/metric/v1url:https://insights-collector.eu01.nr-data.net/metric/v1header:# Custom http headers- name:X-My-Headervalue:Header valueattribute:static:# Static attributes/dimensions to attach to the metrics data sent to New Relic.- name:envvalue:prodpostNewRelicEvent:# Setings used by post event to New Relic API endpointdestinationAccount:- First NR account- Second NR account# Note that the URL path should *not* be included in the url setting below!# As of this writing the valid options are# https://insights-collector.eu01.nr-data.net# https://insights-collector.newrelic.com url:https://insights-collector.eu01.nr-data.net/header:# Custom http headers- name:X-My-Headervalue:Header valueattribute:static:# Static attributes/dimensions to attach to the metrics data sent to New Relic.- name:envvalue:prod......
2.3.6 - Reload alerts
Butler handles reload alerts from both client-managed Qlik Sense and Qlik Sense Cloud.
The same kind of message templates are used, meaning that the look and feel of the alerts are the same regardless of where the alert originated.
This can be of particular interest for companies with a hybrid setup or that are in the process of migrating from client-managed to cloud-based Qlik Sense.
Getting the same kind of alerts from both environments can make it easier to understand what’s going on.
2.3.6.1 - Reload alerts for client-managed Qlik Sense
Butler offers a lot of flexibility when it comes to alerts when reloads fail, are aborted or succeed in Qlik Sense Enterprise on Windows (QSEoW).
Learn how to set up the desired features, the alert layout, formatting and more.
Alert types
These alert types are available:
Reload task failure. Send alerts when reload tasks fail, no matter if they were started on schedule or manually from the QMC.
Reload task aborted. Send alerts when reload tasks are manually aborted in the QMC.
Reload task success. Send alerts when reload tasks complete successfully.
Alert destinations and options
Alerts can be sent to these destinations, with different options available for each destination.
Each destination can be individually enabled/disabled in the config file.
The failed reload’s script log is available in InfluxDb.
New Relic
β
β
β
β
The failed reload’s script log is available in New Relic.
Signl4
β
β
β
β
Alerts are presented in Signl4’s own format in their mobile app.
Slack
β
β
β
β
MS Teams
β
β
β
β
Outgoing webhook
β
β
Formatting is not relevant for webhooks
MQTT
β
β
Formatting is not relevant for MQTT messages
How it works
In order for Butler initiated alerts to become a reality, Butler must somehow be notified that the event of interest (for example a failed reload task) has occurred.
This is achieved by adding a log appender to Qlik Sense Enterprise on Windows.
Log appenders offer a way to hook into Qlik Sense’s logging subsystem, which is called log4net.
By adding a carefully crafted .xml file in the right location on the Sense server(s), you can make Sense notify Butler by means of UDP messages when the events of interest occur. Conceptually it looks like this:
So what happens when a scheduled reload task fails?
Let’s look at the steps:
A reload task is started by the Sense scheduler, either on a time schedule, as a result of some other task(s) finishing or manually by a user in the QMC or from the Hub.
When the task’s state changes, entries are written to the Sense scheduler’s log files using log4net (which is built into Qlik Sense). If the filter defined in the log appender (= the .xml file on the Sense server) matches the log entry at hand, the associated action in the log appender will be carried out.
Log appenders can do all kinds of things, everything from writing custom log files, sending basic emails, writing to databases and much more.
Here we’re interested in the log appender sending a UDP message from Qlik Sense to Butler.
The log appender provided as part of Butler will make log4net send a UDP message to Butler, including various info (reload task ID, timestamp, host name etc) about the reload task that just failed or was stopped/aborted.
Butler will look at the incoming event and determine what it is about.
For example: Is the event about a reload task failure, a reload that has been aborted/stopped, or something else?
Butler thus first works as a dispatcher. In a second step, after the initial dispatch, the event is sent to the relevant handler function within Butler.
Response times are usually very good - Butler will typically get the UDP message within a few seconds after (for example) the reload failing, with alerts going out shortly thereafter.
Warning
The log appenders that catch failed and aborted reloads in the Qlik Sense engine and scheduler must be set up on all Qlik Sense servers where reloads are happening for this feature to work.
Failing to do so will result in Butler not being notified about some reload failures/aborted reloads.
Tip
The concept above is the same also for aborted and successful reload tasks.
Adding a log appender
This is possibly the trickiest part to get right when it comes to setting up log4net based alerts.
Still, if you start from the sample .xml file provided in the Butler repository on GitHub it’s not too hard.
Those sample .xml files are also included in the release Zip files available on the Butler releases page.
The steps are:
In this case you want to be notified when certain events occur in the scheduler log files.
This is important: Qlik Sense Enterprise on Windows consists of many different subsystems (engine, proxy, scheduler, printing etc) - here we’re interested in log events from the scheduler subsystem.
Add a file LocalLogConfig.xml in the C:\ProgramData\Qlik\Sense\Scheduler folder on the Sense server whose scheduler you want to get events from. If you have multiple Sense servers with schedulers running on them, the .xml file should be deployed on each server (assuming you want events from all the servers).
The contents of LocalLogConfig.xml will determine what events are forwarded to Butler, or what other actions will be taken by log4net. See below for examples.
Sense will eventually detect and load the new xml file, but it might take a while (minutes). Restarting the Qlik Sense Scheduler Windows service will make the changes take effect immediately.
Forwarding reload task events to Butler
Here’s the XML that should go into C:\ProgramData\Qlik\Sense\Scheduler\LocalLogConfig.xml to enable the various kinds of Butler task reload alerts.
The remoteAddress property should be set to the host name or IP where Butler is running.
The remotePort property should match the port number specified in Butler’s config file. Note that Butler uses different ports for task related and user activity related events.
The first appender looks for the text “Max retries reached” in the System.Scheduler.Scheduler.Master.Task.TaskSession log stream. That log entry will be created when a reload task has failed and also carried out all its retries. Once the search string is found a UDP message will be sent to port 9998 on IP 10.11.12.13.
The second appender looks for “Execution State Change to Aborting” in the System.Scheduler.Scheduler.Master.Task.TaskSession log stream. That log entry occurs when a user stops a running reload from the QMC’s task view, or using the Sense APIs. When the search string is found a UDP message is once again sent to 10.11.12.13:9998, but with a different messsage (as specified in the conversionpattern property of the appender).
The third appender looks for “Reload complete” in the System.Scheduler.Scheduler.Slave.Tasks.ReloadTask log stream.
That log entry occurs when a reload task has completed successfully.
Here is an XML file that would forward log events as UDP messages to Butler:
<?xml version="1.0" encoding="UTF-8"?><configuration><!-- Appender for detecting reload task failures. Only the last of potentially several retries is reported --><appendername="TaskFailureLogger"type="log4net.Appender.UdpAppender"><filtertype="log4net.Filter.StringMatchFilter"><paramname="stringToMatch"value="Max retries reached"/></filter><filtertype="log4net.Filter.DenyAllFilter"/><paramname="remoteAddress"value="<IP of server where Butler is running>"/><paramname="remotePort"value="9998"/><paramname="encoding"value="utf-8"/><layouttype="log4net.Layout.PatternLayout"><converter><paramname="name"value="hostname"/><paramname="type"value="Qlik.Sense.Logging.log4net.Layout.Pattern.HostNamePatternConverter"/></converter><paramname="conversionpattern"value="/scheduler-reload-failed/;%hostname;%property{TaskName};%property{AppName};%property{User};%property{TaskId};%property{AppId};%date;%level;%property{ExecutionId};%message"/></layout></appender><!-- Appender for detecting aborted reloads --><appendername="AbortedReloadTaskLogger"type="log4net.Appender.UdpAppender"><filtertype="log4net.Filter.StringMatchFilter"><paramname="stringToMatch"value="Execution State Change to Aborting"/></filter><filtertype="log4net.Filter.DenyAllFilter"/><paramname="remoteAddress"value="<IP of server where Butler is running>"/><paramname="remotePort"value="9998"/><paramname="encoding"value="utf-8"/><layouttype="log4net.Layout.PatternLayout"><converter><paramname="name"value="hostname"/><paramname="type"value="Qlik.Sense.Logging.log4net.Layout.Pattern.HostNamePatternConverter"/></converter><paramname="conversionpattern"value="/scheduler-reload-aborted/;%hostname;%property{TaskName};%property{AppName};%property{User};%property{TaskId};%property{AppId};%date;%level;%property{ExecutionId};%message"/></layout></appender><!-- Appender for detecting successful reload tasks --><appendername="ReloadTaskSuccessLogger"type="log4net.Appender.UdpAppender"><filtertype="log4net.Filter.StringMatchFilter"><paramname="stringToMatch"value="Execution State Change to FinishedSuccess"/></filter><filtertype="log4net.Filter.DenyAllFilter"/><paramname="remoteAddress"value="<IP of server where Butler is running>"/><paramname="remotePort"value="9998"/><paramname="encoding"value="utf-8"/><layouttype="log4net.Layout.PatternLayout"><converter><paramname="name"value="hostname"/><paramname="type"value="Qlik.Sense.Logging.log4net.Layout.Pattern.HostNamePatternConverter"/></converter><paramname="conversionpattern"value="/scheduler-reloadtask-success/;%hostname;%property{TaskName};%property{AppName};%property{User};%property{TaskId};%property{AppId};%date;%level;%property{ExecutionId};%message"/></layout></appender><!-- Send message to Butler on task failure --><!-- Send message to Butler on task abort --><!-- Send message to Butler on reload task success --><loggername="System.Scheduler.Scheduler.Master.Task.TaskSession"><appender-refref="TaskFailureLogger"/><appender-refref="AbortedReloadTaskLogger"/><appender-refref="ReloadTaskSuccessLogger"/></logger></configuration>
The above configuration is enough to support all task reload alerts currently supported by Butler.
Sending basic alert emails from Qlik Sense/log4net
If you are happy with the more basic/limited reload-failed alert emails provided by log4net, you can add a SMTP appender like this (the example below is for sending emails using Google GMail, customise as needed).
Note
If sending alert emails from Log4Net you will not get any of the nice formatting, script logs or other features that Butler provides in its alerts.
The email will instead just tell you that a task failed, and include some basic information about the task (task name, specifically).
<?xml version="1.0"?><configuration><!-- Mail appender--><appendername="MailAppender"type="log4net.Appender.SmtpAppender"><filtertype="log4net.Filter.StringMatchFilter"><paramname="stringToMatch"value="Message from ReloadProvider"/></filter><filtertype="log4net.Filter.DenyAllFilter"/><evaluatortype="log4net.Core.LevelEvaluator"><paramname="threshold"value="ERROR"/></evaluator><paramname="to"value="<email address to send failed task notification emails to>"/><paramname="from"value="<sender email address used in notification emails>"/><paramname="subject"value="Qlik Sense failed task (server <servername>)"/><paramname="smtpHost"value="smtp.gmail.com"/><paramname="port"value="587"/><paramname="EnableSsl"value="true"/><paramname="Authentication"value="Basic"/><paramname="username"value="<Gmail username>"/><paramname="password"value="<Gmail password>"/><paramname="bufferSize"value="0"/><!-- Set this to 0 to make sure an email is sent on every error --><paramname="lossy"value="true"/><layouttype="log4net.Layout.PatternLayout"><paramname="conversionPattern"value="%newline%date %-5level %newline%property{TaskName}%newline%property{AppName}%newline%message%newline%newline%newline"/></layout></appender><!--Send mail on task failure--><loggername="System.Scheduler.Scheduler.Slave.Tasks.ReloadTask"><appender-refref="MailAppender"/></logger></configuration>
References
Qlik’s documenation around log appenders and how to hook into the Sense logs is somewhat brief, but does provide a starting point if you want to dive deeper into this topic.
The main log4net documentation (log4net is the logging framework used by Qlik Sense Enterprise) can also be useful.
These links describe how emails can be sent from the log4net logging framework itself, directly to the recipient. Butler includes sameple XML files for this use case too, but Butler takes things further by using the data in the Sense logs to pull in more data around the failed or stopped reload.
In other words - Butler’s alert emails are significantly more flexible and contain information (such as script logs) that are not availble using purely log4net.
2.3.6.1.1 - Reload alerts sent as emails
Description of the various kinds of alert emails Butler can send.
What’s this?
Butler can send two kinds of alert emails:
When a reload task fails during execution.
When a running reload task is somehow stopped/aborted.
When a reload task completes successfully.
See the Concepts section for additional details and sample alert emails.
Basic vs formatted email alerts
If you want Butler to send email alerts you must provide an email template file.
For some other alert destinations (Slack and Teams) Butler offers a “basic” option. A fixed format alert is then sent by Butler.
The closest thing available for emails is to use the mail log appender described here, but if you set up a log appender AND have Butler running, you might as well use the formatted email option as it provides much more flexibility than log4net’s email appender.
Rate limiting and de-duplication
Butler has rate limiting feature to ensure alert recipients are not spammed with too many alert emails.
The rate limit is configured (in seconds) in the main config file and can be set independently for reload-failed and reload-aborted emails.
The corresponding config settings are Butler.emailNotification.reloadTaskFailure.rateLimit, Butler.emailNotification.reloadTaskAborted.rateLimit and Butler.emailNotification.reloadTaskSuccess.rateLimit.
Rate limiting is done based on task ID + email address.
Butler also has a de-duplication feature that ensure each email address that has qualified for an alert email only gets ONE email per alert.
Sending test emails to verify correct settings
It can be tricky to find the correct settings to use Butler with email servers.
Butler itself uses a very generic email components to send emails, but corporate email servers may impose restrictions on from where/what servers emails will be accepted, encryption may be used together with non-standard network ports etc.
Butler offers a command line option that when used will send a simple test email to the specified email address.
This makes is very easy to test if the email settings in Butler’s config file are working or not.
When this command line option is used Butler will start normally, but also send a test email during startup.
The command line option is --test-email-address <address>.
The sender of the test email can be specified with --test-email-from-address <address>.
If the settings in the config file’s Butler.emailNotification.smtp section are valid and correct a command like this can be used: butler.exe -c ./config/production.yaml --test-email-address myname@somedomain.com. Adapt config file location and email address as needed.
The resulting email looks like this:
Sending alert emails to app owners
Butler can optionally send alert emails to the owner of apps that fail reloading/were aborted.
Note
App owner notification email can only be sent to app owners that have an email stored in their Qlik Sense user profile.
This is typically the case if the Qlik Sense user directory has been synced from a Microsoft Active Directory - but there is no guarantee this is the case.
If there is no email available for an app owner, he/she will simply not receive any alert emails.
This feature is controlled by the config file properties Butler.emailNotification.reloadTaskAborted.appOwnerAlert.enable and Butler.emailNotification.reloadTaskFailure.appOwnerAlert.enable.
If set to true the app owner will be added to the send list of alert emails, in addition to the recipients specied in Butler.emailNotification.reloadTaskAborted.recipients and Butler.emailNotification.reloadTaskFailure.recipients.
The sections of the config file dealing with app owner notification emails looks like this:
appOwnerAlert:enable:true# Should app owner get notification email (assuming email address is available in Sense user directory)includeOwner:includeAll:true# true = Send notification to all app owners except those in exclude list# false = Send notification to all app owners in the include listuser:- directory:<Sense user directory>userId:<userId>- directory:<Sense user directory>userId:<userId>excludeOwner:user:- directory:<Sense user directory>userId:<userId>- directory:<Sense user directory>userId:<userId>
It works like this:
If appOwnerAlert.enable is set to false no app owner emails will be sent. If it’s set to true the rules below apply.
If appOwnerAlert.includeOwner.includeAll is set to true all app owners will get notification emails when apps the own fail/are aborted…
… except those app owners listed in the appOwnerAlert.excludeOwner.user array.
That array thus provides a way to exclude some app owners (e.g. system accounts) to receive notifcation emails.
If appOwnerAlert.includeOwner.includeAll is set to false it’s still possible to add individual app owners to the appOwnerAlert.includeOwner.user array.
Those users will then receive notification emails for apps they own.
Send alerts only for some reload tasks
Some reload tasks may be more important than others.
I.e. some tasks should generate alert emails when they fail/abort/succeed, but others not.
Butler controls which tasks to send alerts for by looking at a specific Qlik Sense custom property.
Note
The concept described below is the same for failed, aborted and successful reload tasks.
Each of these three types of tasks have their own settings in the config file.
If the config file setting Butler.emailNotification.reloadTaskFailure.alertEnableByCustomProperty.enable is set to false, all failed reload tasks will cause alert emails.
If that setting is true only some tasks will cause alert emails:
If a task has the value specified in Butler.emailNotification.reloadTaskFailure.alertEnableByCustomProperty.enabledValue set for the custom property named as specified in Butler.emailNotification.reloadTaskFailure.alertEnableByCustomProperty.customPropertyName, the alert will be sent.
If a task does not have that custom property set, no alert will be sent for that task.
A task can still cause an alert to be sent if a specific email address is specified for the task, see below for details.
Some configuration is needed to make this work:
Make changes to the config file. Specifically the three settings mentioned above needs to be reviewed and updated as needed.
Create a custom property in Sense.
The name and value of the custom property must match the one in the config file, Butler.emailNotification.reloadTaskFailure.alertEnableByCustomProperty.customPropertyName and Butler.emailNotification.reloadTaskFailure.alertEnableByCustomProperty.enabledValue.
The custom property should be available on reload tasks.
Set the custom property for reload tasks for which alert emails should be sent.
Aborted reload tasks (as compared to the failed reload tasks described above) are handled the same way, with their own settings in the config file.
In the QMC the custom property can look like this:
Send alerts to specific people, for some tasks
It’s possible to send alert emails to specific email addresses and control this on a per-task basis.
This is achieved by using a Sense custom property that contains the email addresses alerts should be sent to, for the task in question.
Note
The concept described below is the same for failed, aborted and successful reload tasks.
Each of these three types of tasks have their own settings in the config file.
These config setting Butler.emailNotification.reloadTaskFailure.alertEnableByEmailAddress.customPropertyName controls which custom property is used to store email addresses for failed reload tasks.
Email specific alert recpients is independent from the feature where alerts can be switched on/off for individual tasks (see above).
In other words: If an email address has been designated as recipient of alert emails, that address will always receive alert emails for all failed reload tasks.
Having set two different (blurred out) recipients of alert emails for a reload task:
Settings in config file
Warning
Don’t forget to create the log appender .xml files on the Sense server(s). This page describes how.
Those xml files are the foundation on top of which all Butler reload task alerts are built - without them the alerts described on this page won’t work.
---Butler:......# Qlik Sense related links used in notification messagesqlikSenseUrls:qmc:<Link to Qlik Sense QMC>hub:<Link to Qlik Sense Hub>......# Settings needed to send email notifications when for example reload tasks fail.# Reload failure notifications assume a log appender is configured in Sense AND that the UDP server in Butler is running.emailNotification:enable:falsereloadTaskSuccess:enable:false# Custom property used to control which task successes will cause alert emails to be sent# If this setting is true, alerts will not be sent for all tasks, but *only* for tasks with the CP set to the enabledValue.# If this setting is false, alerts will be sent for all failed reload tasks.alertEnableByCustomProperty:enable:falsecustomPropertyName:'Butler_SuccessAlertEnableEmail'enabledValue:'Yes'# Custom property used to say that alerts for a certain task should be sent to zero or more recipients# These alerts will be sent irrespective of the alertEnableByCustomProperty.enable setting.alertEnabledByEmailAddress:customPropertyName:'Butler_SuccessAlertSendToEmail'rateLimit:60# Min seconds between emails for a given taskID. Defaults to 5 minutes.headScriptLogLines:15tailScriptLogLines:25priority:high # high/normal/lowsubject: 'β Qlik Sense reload success:"{{taskName}}"'
bodyFileDirectory: path/to/email_templates
htmlTemplateFile: success-reload-qseow
fromAddress: Qlik Sense (no-reply) <qliksense-noreply@ptarmiganlabs.com>
recipients:
- <Email address 1>
- <Email address 2>
reloadTaskAborted:
enable: false
appOwnerAlert:
enable: true # Should app owner get notification email (assuming email address is available in Sense user directory)
includeOwner:
includeAll: true # true = Send notification to all app owners except those in exclude list
# false = Send notification to app owners in the include list
user:
- directory: <Sense user directory>
userId: <userId>
- directory: <Sense user directory>
userId: <userId>
excludeOwner:
user:
- directory: <Sense user directory>
userId: <userId>
- directory: <Sense user directory>
userId: <userId>
# Custom property used to control which aborted tasks will cause alert emails to be sent
# If this setting is true, alerts will not be sent for all tasks, but *only* for tasks with the CP set to the enabledValue.
# If this setting is false, alerts will be sent for all aborted reload tasks.
alertEnableByCustomProperty:
enable: true
customPropertyName: 'Butler_AbortedAlertEnableEmail'enabledValue:'Yes'# Custom property used to say that alerts for a certain task should be sent to zero or more recipients# These alerts will be sent irrespective of the alertEnableByCustomProperty.enable setting.alertEnabledByEmailAddress:customPropertyName:'Butler_AbortedAlertSendToEmail'rateLimit:600# Min seconds between emails for a given taskID. Defaults to 5 minutes.headScriptLogLines:15# Number of lines from start of script to include in emailtailScriptLogLines:15# Number of lines from end of script to include in emailpriority:high # high/normal/lowsubject: 'Qlik Sense reload aborted:"{{taskName}}"' # Email subject. Can use template fields
bodyFileDirectory: path/to/email_templates # Directory where email body template files are stored
htmlTemplateFile: aborted-reload # Name of email body template file to use
fromAddress: Qlik Sense (no-reply) <qliksense-noreply@mydomain.com>
recipients: # Array of email addresses to which the notification email will be sent
- <Email address 1>
- <Email address 2>
reloadTaskFailure:
enable: false
appOwnerAlert:
enable: true # Should app owner get notification email (assuming email address is available in Sense user directory)
includeOwner:
includeAll: true # true = Send notification to all app owners except those in exclude list
# false = Send notification to app owners in the include list
user:
- directory: <Sense user directory>
userId: <userId>
- directory: <Sense user directory>
userId: <userId>
excludeOwner:
user:
- directory: <Sense user directory>
userId: <userId>
- directory: <Sense user directory>
userId: <userId>
# Custom property used to control which task failures will cause alert emails to be sent
# If this setting is true, alerts will not be sent for all tasks, but *only* for tasks with the CP set to the enabledValue.
# If this setting is false, alerts will be sent for all failed reload tasks.
alertEnableByCustomProperty:
enable: false
customPropertyName: 'Butler_FailedAlertEnableEmail'enabledValue:'Yes'# Custom property used to say that alerts for a certain task should be sent to zero or more recipients# These alerts will be sent irrespective of the alertEnableByCustomProperty.enable setting.alertEnabledByEmailAddress:customPropertyName:'Butler_FailedAlertSendToEmail'rateLimit:600# Min seconds between emails for a given taskID. Defaults to 5 minutes.headScriptLogLines:15# Number of lines from start of script to include in emailtailScriptLogLines:15# Number of lines from end of script to include in emailpriority:high # high/normal/lowsubject: 'Qlik Sense reload failed:"{{taskName}}"' # Email subject. Can use template fieldsbodyFileDirectory:path/to/email_templates # Directory where email body template files are storedhtmlTemplateFile:failed-reload # Name of email body template file to usefromAddress:Qlik Sense (no-reply) <qliksense-noreply@mydomain.com>recipients:# Array of email addresses to which the notification email will be sent- <Email address 1>- <Email address 2>......smtp:# Email server settings. See https://nodemailer.com/smtp/ for details on the meaning of these fields.host:<FQDN or IP or email server, e.g. smtp.gmail.com>port:<port on which SMTP server is listening>secure:true# true/falsetls:serverName:# If specified the serverName field will be used for TLS verification instead of the host field.ignoreTLS:falserequireTLS:truerejectUnauthorized:falseauth:enable:trueuser:<Username, email address etc>password:<your-secret-password>......udpServerConfig:enable:false# Should the UDP server responsible for receving task failure and session events be started? true/falseserverHost:<FQDN or IP (or localhost) of server where Butler is running>portTaskFailure:9998......
Templates: Configuring email appearance
Alert emails use standard HTML formatting. Inline CSS can be used (if so desired) for fine tuning the visual look of the alert email.
Butler’s process for sending alert emails is
Figure out which email body template file should be used. This is determine by two set of fields in the main config file:
For reload failure emails these config file properties are used:
Butler.emailNotification.reladTaskFailure.bodyFileDirectory and
Butler.emailNotification.reladTaskFailure.htmlTemplateFile. A .handlebars extension is assumed.
For aborted reload emails these config file properties are used:
Butler.emailNotification.reloadTaskAborted.bodyFileDirectory and
Butler.emailNotification.reloadTaskAborted.htmlTemplateFile. A .handlebars extension is assumed.
For successful reload emails these config file properties are used:
Butler.emailNotification.reloadTaskSuccess.bodyFileDirectory and
Butler.emailNotification.reloadTaskSuccess.htmlTemplateFile. A .handlebars extension is assumed.
For email subjects, these config properties are used:
Butler.emailNotification.reladTaskFailure.subject, Butler.emailNotification.reloadTaskAborted.subject and Butler.emailNotification.reloadTaskSuccess.subject.
Process the body template, replacing template fields with actual values.
Process the email subject template, replacing template fields with actual values.
Send the email.
A couple of sample template files are found in the src/config/email_templates directory of the GitHub repository.
Tip
You can use template fields in email subjects too!
Using custom links in templates
Links to Qlik Sense QMC and Hub (for both client-managed and Qlik Sense Cloud) can be included in email templates.
It is also possible to define custom links in the config file, and use them in email templates.
This is described here: Custom links in alerts.
Template fields reference
A complete list of template fields - including descriptions - is available in the Reference section.
2.3.6.1.2 - Reload alerts in InfluxDB
Description of how information of how successful and failed reload tasks can be stored in InfluxDB.
What’s this?
Butler can store information about both successful and failed reload tasks in InfluxDB.
If enabled, Butler will store information about all failed reload tasks to InfluxDB.
For successful reload tasks, there are two options:
Store information about all successful reload tasks to InfluxDB.
Store information about some successful reload tasks to InfluxDB.
Which tasks to store information about is controlled using a custom property on the reload task.
Once the information about the reload task is in InfluxDB it can be used in Grafana dashboards.
This way it is possible to get a good, continuous overview of the reload activity in your Qlik Sense environment.
You can also use the information to create alerts in Grafana using it’s comprehensive alerting capabilities, including alerting to Slack, Teams, email, etc.
Please note that InflixDB must be enabled and correctly configured in the Butler config file for the below features to work.
Monitor failed reload tasks
If enabled using the Butler.influxDb.reloadTaskFailure.enable setting, Butler will store information about all failed reload tasks in InfluxDB.
The information stored includes (among other things):
The name and ID of the app that the failed reload task was reloading.
The name and ID of the reload task.
The name of the Qlik Sense node/server that the task was running on.
User who started the reload task. This will be the service account when the task was started by a schedule or via a task chain/trigger.
Execution ID of the reload. This is a unique ID that is generated by Qlik Sense for each reload task execution, it can be used to cross-reference the reload task with related entries in the Qlik Sense log files.
Last Butler.influxDb.reloadTaskFailure.tailScriptLogLines lines of the Sense log file for the reload task.
Static tags defined in the config file’s Butler.influxDb.reloadTaskFailure.tag.static section.
Dynamic app tags, i.e. Sense tags for the app being reloaded, if enabled in the config file Butler.influxDb.reloadTaskFailure.tag.dynamic.useAppTags section.
Dynamic reload task tags, i.e. Sense tags for the reload task being executed, if enabled in the config file Butler.influxDb.reloadTaskFailure.tag.dynamic.useTaskTags section.
A complete definition of all information sent to InfluxDB is available in the reference section.
Monitor successful reload tasks
Butler can monitor all reload tasks for successful completion, or only some of them.
Monitor all successful reload tasks
If enabled using the Butler.influxDb.reloadTaskSuccess.allReloadTasks.enable setting, Butler will store information about all successful reload tasks in InfluxDB.
The information stored is almost the same as for failed reload tasks, except that the Sense script log file is not included.
Monitor only some successful reload tasks
If enabled using the Butler.influxDb.reloadTaskSuccess.byCustomProperty.enable setting, Butler will store information about only some successful reload tasks in InfluxDB.
Which tasks to store information about is controlled using a custom property on the reload task.
The name of the custom property is defined in the Butler.influxDb.reloadTaskSuccess.byCustomProperty.customPropertyName setting.
The value of the custom property that will be used to indicate that the reload task should be monitored is defined in the Butler.influxDb.reloadTaskSuccess.byCustomProperty.enabledValue setting.
Static vs dynamic tags
Butler offers two kinds of tags: Static and dynamic.
Static tags are defined in the config file and are the same for all messages stored in InfluxDB.
An example of a static tag could be the name of the Qlik Sense server that Butler is running on, or whether the message related to a production or test Qlik Sense environment.
Dynamic attributes are determined at run-time when the message is stored in InfluxDB.
Settings in config file
---Butler:......influxDb:......reloadTaskFailure:enable:truetailScriptLogLines:20tag:static:# Static tags to attach to data stored in InflixDB- name:butler_instancevalue:prod-1dynamic:useAppTags:true# Should app tags be stored in InfluxDB as tags?useTaskTags:true# Should task tags be stored in InfluxDB as tags? reloadTaskSuccess:enable:trueallReloadTasks:enable:falsebyCustomProperty:enable:truecustomPropertyName:'Butler_SuccessReloadTask_InfluxDB'enabledValue:'Yes'tag:static:# Static attributes/dimensions to attach to events sent to InfluxDb# - name: event-specific-tag 1# value: abc 123dynamic:useAppTags:true# Should app tags be sent to InfluxDb as tags?useTaskTags:true# Should task tags be sent to InfluxDb as tags?......
2.3.6.1.3 - Reload alerts via New Relic
Description of how reload alerts can be sent to New Relic as events and log messages.
What’s this?
Butler can send two kinds of messages to New Relic:
When a scheduled or started from the QMC reload task fails.
When a scheduled or started from the QMC reload task is somehow stopped/aborted.
See the Concepts section for examples on what a New Relic alert can look like.
This page has additional info on how to set up Butler to work with New Relic.
A complete reference to the config file format is found here.
Different kinds of New Relic messages
Two kinds of messages can be sent to New Relic: Events and log messages.
The difference between them is that New Relic events are meant to be used for alerting, while New Relic log messages are meant to be used for troubleshooting.
Events are more flexible in terms of what data can be included in them, whereas log messages are just that - parts of Sense log files sent to New Relic.
Together they provide a powerful combination of alerting and troubleshooting capabilities, but they can also be enabled independently of each other.
Destination accounts
New Relic does not have very good access control capabilities for their dashboards, so if you want certain people to see only some reload alerts, and other people to see other alerts, you need to create multiple New Relic accounts.
Butler supports this scenario and can send messages to one or more New Relic accounts.
It is possible to specify per reload task which New Relic account(s) to send alerts to.
Three pieces of information is needed for each New Relic account that Butler should send messages to:
The name of the New Relic account. This is just a name that you choose, it is not used for anything other than to identify the account in Butler’s config file and in the custom properties of Qlik Sense reload tasks.
The New Relic account ID.
The New Relic insert/API key. This is basically a secret key that is used to authenticate Butler with New Relic.
Account numbers and insert keys are available in the New Relic UI, under “Account settings” > “Data sharing”.
Authentication and credentials
Butler looks for New Relic account names, account ID and API keys in two places:
The command line, using the --new-relic-account-name, --new-relic-account-id and --new-relic-api-key options.
If you have multiple New Relic accounts they should be listed in sequence, separated by space.
Account names can include spaces, but should then be enclosed in double quotes.
Example: --new-relic-account-name "First New Relic account" "Second New Relic account" --new-relic-api-key 1234567890abcdef 0987654321fedcba --new-relic-account-id 1234567 7654321
The config file, in the Butler.thirdPartyToolsCredentials.newRelic section.
Standard attributes
When sending messages to New Relic you can include “attributes”.
Attributes are key/value pairs that can be used to provide additional information about the message.
They can be added to both events and log messages.
Attributes can be used in New Relic dashboards to filter and group messages in various ways.
Static vs dynamic attributes
Butler offers two kinds of attributes: Static and dynamic.
Static attributes are defined in the config file and are the same for all messages sent to New Relic.
An example of a static attribute could be the name of the Qlik Sense server that Butler is running on, or whether the message related to a production or test Qlik Sense environment.
Dynamic attributes are determined at run-time when the message is sent to New Relic.
Examples include:
Sense tags that are assigned to the reload task that failed. Their names are qs_appTag_<tag name>
App tags of the app that failed to reload. Their names are qs_taskTag_<tag name>
Shared settings
Some settings are shared between events and log messages, these are found in the sharedSettings sections of the config file.
Values there will be used for both events and log messages, unless they are overridden in the respective events or logMessages sections of the config file.
Settings in config file
---Butler:......thirdPartyToolsCredentials:newRelic:# Array of New Relic accounts/insert keys. Any data sent to New Relic will be sent to both accounts. - accountName:First NR accountinsertApiKey:<API key 1 (with insert permissions) from New Relic> accountId:<New Relic account ID 1>- accountName:Second NR accountinsertApiKey:<API key 2 (with insert permissions) from New Relic> accountId:<New Relic account ID 2>......incidentTool:newRelic:enable:falsedestinationAccount:event:# Failed/aborted reload tasks are sent as events to these New Relic accounts- First NR account- Second NR accountlog:# Failed/aborted reload tasks are sent as log entries to these New Relic accounts- First NR account- Second NR account# New Relic uses different API URLs for different kinds of data (metrics, events, logs, ...)# There are different URLs depending on whther you have an EU or US region New Relic account.# The available URLs are listed here: https://docs.newrelic.com/docs/accounts/accounts-billing/account-setup/choose-your-data-center/url:# As of this writing the valid options are# https://insights-collector.eu01.nr-data.net# https://insights-collector.newrelic.com event:https://insights-collector.eu01.nr-data.net# Valid options are (1) EU/rest of world and 2) US)# https://log-api.eu.newrelic.com/log/v1# https://log-api.newrelic.com/log/v1 log:https://log-api.eu.newrelic.com/log/v1reloadTaskFailure:destination:event:enable:falsesendToAccount:# Which reload task failures are sent to New Relic as eventsbyCustomProperty:enable:false# Control using a task custom property which reload task failures are sent as eventscustomPropertyName:'Butler_FailedTask_Event_NewRelicAccount'always:enable:false# Controls which New Relic accounts ALL failed reload tasks are sent to (as events)account:- First NR account- Second NR accountattribute:static:# Static attributes/dimensions to attach to events sent to New Relic.- name:event-specific-attribute 1 # Examplevalue:abc 123 # Exampledynamic:useAppTags:true# Should app tags be sent to New Relic as attributes?useTaskTags:true# Should task tags be sent to New Relic as attributes?log:enable:falsetailScriptLogLines:20sendToAccount:# Which reload task failures are sent to New Relic as log entriesbyCustomProperty:enable:false# Control using a task custom property which reload task failures are sent as log entriescustomPropertyName:'Butler_FailedTask_Log_NewRelicAccount'always:enable:false# Controls which New Relic accounts ALL failed reload tasks are sent to (as logs)account:- First NR account- Second NR accountattribute:static:# Static attributes/dimensions to attach to events sent to New Relic.- name:log-specific-attribute 1 # Examplevalue:def 123 # Exampledynamic:useAppTags:true# Should app tags be sent to New Relic as attributes?useTaskTags:true# Should task tags be sent to New Relic as attributes?sharedSettings:rateLimit:15# Min seconds between events sent to New Relic for a given taskID. Defaults to 5 minutes.header:# Custom http headers- name:X-My-Header # Examplevalue:Header value 1# Exampleattribute:static:# Static attributes/dimensions to attach to events sent to New Relic.- name:service # Examplevalue:butler # Example- name:environment # Examplevalue:prod # ExamplereloadTaskAborted:destination:event:enable:falsesendToAccount:# Which reload task aborts are sent to New Relic as eventsbyCustomProperty:enable:false# Control using a task custom property which reload task aborts are sent as eventscustomPropertyName:'Butler_AbortedTask_Event_NewRelicAccount'always:enable:false# Controls which New Relic accounts ALL aborted reload tasks are sent to (as events)account:- First NR account- Second NR accountattribute:static:# Static attributes/dimensions to attach to events sent to New Relic.- name:event-specific-attribute 2 # Examplevalue:abc 123 # Exampledynamic:useAppTags:true# Should app tags be sent to New Relic as attributes?useTaskTags:true# Should task tags be sent to New Relic as attributes?log:enable:falsetailScriptLogLines:20sendToAccount:# Which reload task aborts are sent to New Relic as log entriesbyCustomProperty:enable:true# Control using a task custom property which reload task aborts are sent as log entriescustomPropertyName:'Butler_AbortedTask_Log_NewRelicAccount'always:enable:false# Controls which New Relic accounts ALL aborted reload tasks are sent to (as logs)account:- First NR account- Second NR accountattribute:static:# Static attributes/dimensions to attach to events sent to New Relic.- name:log-specific-attribute 2 # Examplevalue:def 123 # Exampledynamic:useAppTags:true# Should app tags be sent to New Relic as attributes?useTaskTags:true# Should task tags be sent to New Relic as attributes?sharedSettings:rateLimit:15# Min seconds between events sent to New Relic for a given taskID. Defaults to 5 minutes.header:# Custom http headers- name:X-My-Header # Examplevalue:Header value 2# Exampleattribute:static:# Static attributes/dimensions to attach to events sent to New Relic.- name:service # Examplevalue:butler # Example- name:environment # Examplevalue:prod # ExampleserviceMonitor:destination:event:enable:falsesendToAccount:# Windows service events are sent to these New Relic accounts- First NR account- Second NR accountattribute:static:# Static attributes/dimensions to attach to events sent to New Relic.- name:event-specific-attributevalue:abc 123dynamic:serviceHost:true# Should host where service is running be sent to New Relic as attribute?serviceName:true# Should service name be sent to New Relic as attribute?serviceDisplayName:true# Should service display name be sent to New Relic as attribute?serviceState:true# Should service state be sent to New Relic as attribute?log:enable:falsesendToAccount:# Windows service log entries are sent to these New Relic accounts- First NR account- Second NR accountattribute:static:# Static attributes/dimensions to attach to events sent to New Relic.- name:log-specific-attributevalue:def 456dynamic:serviceHost:true# Should host where service is running be sent to New Relic as attribute?serviceName:true# Should service name be sent to New Relic as attribute?serviceDisplayName:true# Should service display name be sent to New Relic as attribute?serviceState:true# Should service state be sent to New Relic as attribute?monitorServiceState:# Control whih service states are sent to New Relicrunning:enable:truestopped:enable:truesharedSettings:rateLimit:5# Min seconds between events/logs sent to New Relic for a given host+service. Defaults to 5 minutes.header:# Custom http headers- name:X-My-Header # Examplevalue:Header value 2 # Exampleattribute:static:# Static attributes/dimensions to attach to events sent to New Relic.- name:service # Examplevalue:butler # Example- name:environment # Examplevalue:prod # Example......
2.3.6.1.4 - Reload alerts via Slack
Description of how reload alerts can be sent as Slack messages.
What’s this?
Butler can send two kinds of alert messages via Slack:
A complete reference to the config file format is found here.
Basic vs formatted Slack alerts
Slack alerts come in two forms:
Customizable formatting using a template concept. A standard template that will fit most use cases is included with Butler. Using this option the first and last parts of the script log can be included in the message, allowing you to tell from the Slack message what caused the reload to fail.
You can also add buttons to the message that can be used to open any URL you want, or open the app that failed reloading.
A fixed, more basic format that is built into Butler. No template file needed, but also less detailed.
Which option to go for depends on whether you want just a notification that something went wrong, or if you want as much detail as possible in the Slack message. In most cases the customizable formatting is the best option.
Sample message with custom formatting
Note
The concept described below is the same for failed and aborted reload tasks.
Each of these have their own settings in the config file.
A “reload task failed” Slack message using the custom formatting option could look like this:
Here’s how to set this up:
Create an incoming webhook in Slack, take note of its URL (you will need it in step 2 below).
Edit the Slack section of the config file, i.e. the settings in Butler.slackNotification.reloadTaskFailure.
The messageType property should be set to formatted.
The basicMsgTemplate property is not used with formatted messages and can thus be left empty,
Edit the template file if/as needed, the file is specified in Butler.slackNotification.reloadTaskFailure.templateFile. It uses the Handlebars templating engine, to which Butler provides template fields with actual values.
Sample template files are included in the release Zip file, and are also available in the GitHub repository’s src/config/slack_templates directory.
Restart Butler if it’s already running.
Sample message with basic formatting
Note
The concept described below is the same for failed and aborted reload tasks.
Each of these have their own settings in the config file.
A “reload task failed” Slack message with basic formatting could look like this:
To set it up:
Create an incoming webhook in Slack if you don’t already have one, take note of its URL (you will need it in step 2 below).
Edit the Slack section of the config file, i.e. in Butler.slackNotification.reloadTaskFailure.
The messageType property should be set to basic.
The basicMsgTemplate property is the message that will be sent via Slack. Template fields can be used.
Restart Butler if it’s already running.
Customizing Slack messages
When using the formatted Slack alerts you have full freedom to create the alert you need.
Behind the scenes Slack messages are constructed from blocks defined in a JSON object. Each block can then contain either plain text, Markdown, images, buttons etc.
The Slack documentation is the best place for learning how to customize messages.
When it comes to Butler, it uses the Handlebars templating engine to render the template files into Slack JSON objects that are then sent to Slack via their APIs.
A few things to keep in mind when creating custom Slack messages:
The handlebars syntax itself must be correct. If incorrect no Slack JSON object will be created. And no Slack messages sent.
The handlebars template must result in a JSON object that adheres to Slack’s API specifications.
If the JSON syntax is somehow invaid the Slack API will return errors and no messages sent. JSON can be pretty sensitive to details, there should for example not be any trailing commas in properly formatted JSON objects.
Block Kit Builder: Great sandbox wtih readily available examples of different message layouts, syntax and more. Note: You must be logged into your Slack account to use this tool.
Using custom links in templates
It is also possible to define custom links in the config file, and use them in Slack templates.
This is described here: Custom links in alerts.
How it works
Warning
Don’t forget to create the log appender .xml files on the Sense server(s).
Those xml files are the foundation on top of which all Butler alerts are built - without them the alerts described on this page won’t work.
The concept is the same for all alert types, see the email alerts for details.
Settings in config file
---Butler:......# Settings for notifications and messages sent to SlackslackNotification:enable:falserestMessage:webhookURL:<web hook URL from Slack> # Webhook to use when sending basic Slack messages via Butler's REST API reloadTaskFailure:# Reload task failed in QSEoWenable:falsewebhookURL:<web hook URL from Slack>channel:sense-task-failure # Slack channel to which task failure notifications are sentmessageType:formatted # formatted / basic. Formatted means that template file below will be used to create the message.basicMsgTemplate: 'Qlik Sense reload failed:"{{taskName}}"' # Only needed if message type = basic
rateLimit: 300 # Min seconds between emails for a given taskID. Defaults to 5 minutes.
headScriptLogLines: 10
tailScriptLogLines: 10
templateFile: /path/to/slack/template/directory/failed-reload-qseow.handlebars
fromUser: Qlik Sense
iconEmoji: ':ghost:'reloadTaskAborted:# Reload task aborted in QSEoWenable:falsewebhookURL:<web hook URL from Slack>channel:sense-task-aborted # Slack channel to which task stopped notifications are sentmessageType:formatted # formatted / basic. Formatted means that template file below will be used to create the message.basicMsgTemplate: 'Qlik Sense reload aborted:"{{taskName}}"' # Only needed if message type = basic
rateLimit: 300 # Min seconds between emails for a given taskID. Defaults to 5 minutes.
headScriptLogLines: 10
tailScriptLogLines: 10
templateFile: /path/to/slack/template/directory/aborted-reload-qseow.handlebars
fromUser: Qlik Sense
iconEmoji: ':ghost:'......udpServerConfig:enable:false# Should the UDP server responsible for receving task failure and session events be started? true/falseserverHost:<FQDN or IP (or localhost) of server where Butler is running>portTaskFailure:9998......
2.3.6.1.5 - Reload alerts via Microsoft Teams
Description of how reload alerts can be sent as Microsoft Teams messages.
What’s this?
Butler can send two kinds of alert messages via Teams:
A complete reference to the config file format is found here.
Basic vs formatted Teams alerts
Teams alerts come in two forms:
Customizable formatting using a template concept. A standard template that will fit most use cases is included with Butler. With this option the first and last parts of the script log can be included in the message, allowing you to tell from the Teams message what caused the reload to fail.
You can also add buttons to the message that can be used to open any URL you want, or open the app that failed reloading.
A fixed, more basic format that is built into Butler. No template file needed.
Which option to go for depends on whether you want just a notification that something went wrong, or if you want as much detail as possible in the Teams message.
Sample message with custom formatting
Note
The concept described below is the same for failed and aborted reload tasks.
Each of these have their own settings in the config file.
A “reload task failed” Teams message using the custom formatting option could look like this:
Here’s how to set it up:
Create a workflow in Teams, take note of its URL (you will need it in step 2 below). More information on how to create a Teams workflow in the Concepts section.
Edit the Teams section of the config file, i.e. the settings in Butler.teamsNotification.reloadTaskFailure.
The messageType property should be set to formatted.
The basicMsgTemplate property is not used with formatted messages and can thus be left empty,
Edit the template file if/as needed, the file is specified in Butler.teamsNotification.reloadTaskFailure.templateFile.It uses the Handlebars templating engine, to which Butler provides template fields with actual values.
Sample template files are included in the release Zip file, and are also available in the GitHub repository’s src/config/teams_templates directory.
Restart Butler if it’s already running.
Sample message with basic formatting
Note
The concept described below is the same for failed and aborted reload tasks.
Each of these have their own settings in the config file.
A “reload task failed” Teams message with basic formatting could look like this:
To set it up:
Create an incoming webhook in Teams if you don’t already have one, take note of its URL (you will need it in step 2 below).
Edit the Teams section of the config file i.e. the settings in Butler.teamsNotification.reloadTaskFailure and/or Butler.teamsNotification.reloadTaskAborted sections of the confi file.
The messageType property should be set to basic.
The basicMsgTemplate property is the message that will be sent via Teams. Template fields can be used.
Restart Butler if it’s already running.
Customizing Teams messages
When using the formatted Teams alerts you have full freedom to create the alert you need.
Behind the scenes Teams messages are constructed as “Adaptive Cards”, which is standardised JSON format that Teams understands.
More information on Adaptive Cards can be found here, here and here.
When it comes to Butler, it uses the Handlebars templating engine to render a template file into an adaptive card JSON object that is then sent to the workflow webhook.
A few things to keep in mind when creating custom Teams messages:
The handlebars syntax itself must be correct. If incorrect no Teams JSON object will be created. And no Teams message sent.
The handlebars template must result in a JSON object that adheres to Teams’s specifications for JSON payloads.
If the JSON syntax is somehow invaid the Teams API will return errors and no messages sent. JSON can be pretty sensitive to details, there should for example not be any trailing commas in properly formatted JSON objects.
Using custom links in templates
It is also possible to define custom links in the config file, and use them in Teams templates.
This is described here: Custom links in alerts.
How it works
Warning
Don’t forget to create the log appender .xml files on the Sense server(s).
---Butler:......# Settings for notifications and messages sent to MS TeamsteamsNotification:enable:falsereloadTaskFailure:enable:falsewebhookURL:<web hook URL from MS Teams>messageType:formatted # formatted / basic. Formatted means that template file below will be used to create the message.basicMsgTemplate: 'Qlik Sense reload failed:"{{taskName}}"' # Only needed if message type = basic
rateLimit: 300 # Min seconds between emails for a given taskID. Defaults to 5 minutes.
headScriptLogLines: 10
tailScriptLogLines: 10
templateFile: /path/to/teams/template/directory/failed-reload-qseow.handlebars
reloadTaskAborted:
enable: false
webhookURL: <web hook URL from MS Teams>
messageType: formatted # formatted / basic. Formatted means that template file below will be used to create the message.
basicMsgTemplate: 'Qlik Sense reload aborted:"{{taskName}}"' # Only needed if message type = basicrateLimit:300# Min seconds between emails for a given taskID. Defaults to 5 minutes.headScriptLogLines:10tailScriptLogLines:10templateFile:/path/to/teams/template/directory/aborted-reload-qseow.handlebars......udpServerConfig:enable:false# Should the UDP server responsible for receving task failure and session events be started? true/falseserverHost:<FQDN or IP (or localhost) of server where Butler is running>portTaskFailure:9998......
2.3.6.1.6 - Reload alerts via MQTT
Description of how reload alerts can be sent as MQTT messages.
What’s this?
Butler can send two kinds of alert messages as MQTT messages:
When a scheduled, running reload task fails.
When a scheduled, running reload task is somehow stopped.
How it works
Basic message
The MQTT message will be sent on the MQTT topic defined in the config file property Butler.mqttConfig.taskAbortedTopic or Butler.mqttConfig.taskFailureTopic, depending on the event type.
The task name will be sent in the message body.
The basic message looks like this when viewed in the MQTTLens app:
Complete message
Optionally a larger, more complete message is also sent if Butler.mqttConfig.taskFailureSendFull or Butler.mqttConfig.taskFailureSendFull are set to true.
This message contains a stringified JSON of all available information about the failed/aborted task.
The message is sent on the Butler.mqttConfig.taskFailureFullTopic or Butler.mqttConfig.taskAbortedFullTopic topics.
That message can look like this:
Remember
Don’t forget to create the log appender .xml files on the Sense server(s). This page describes how.
Those xml files are the foundation on top of which all Butler alerts are built - without them the alerts described on this page won’t work.
The concept is more or less the same as for alert emails.
Settings in config file
---Butler:......mqttConfig:enable:false# Should Qlik Sense events be forwarded as MQTT messages?brokerHost:<FQDN or IP of MQTT server>brokerPort:1883azureEventGrid:enable:false# If set to true, Butler will connect to an Azure Event Grid MQTT Broker, using brokerHost and brokerPort above clientId:<client ID>clientCertFile:<path to client certificate file>clientKeyFile:<path to client key file>taskFailureSendFull:truetaskAbortedSendFull:truesubscriptionRootTopic:qliksense/# # Topic that Butler will subscribe totaskStartTopic:qliksense/start_task # Topic for incoming messages used to start Sense tasks. Should be subtopic to subscriptionRootTopictaskFailureTopic:qliksense/task_failuretaskFailureFullTopic:qliksense/task_failure_fulltaskFailureServerStatusTopic:qliksense/butler/task_failure_servertaskAbortedTopic:qliksense/task_abortedtaskAbortedFullTopic:qliksense/task_aborted_full......udpServerConfig:enable:false# Should the UDP server responsible for receving task failure and session events be started? true/falseserverHost:<FQDN or IP (or localhost) of server where Butler is running>portTaskFailure:9998......
2.3.6.1.7 - Reload alerts via outgoing webhooks
Description of how reload alerts can be sent via outgoing webhooks.
What’s this?
Butler can send two kinds of alert messages as outgoing webhooks:
When a scheduled, running reload task fails.
When a scheduled, running reload task is somehow stopped/aborted.
How it works
Outgoing webhooks is a concept where Butler will do a GET, POST or PUT HTTP call to a specific URL when a task fails or is aborted/stopped.
The use case is to interface with currently unkown third party systems in a generic way.
Both http and https calls are supported, including the use of self-signed certificates and untrusted certificates.
As the call will include information about the failed/aborted task, the typical (and arguably most correct) way of doing this would be via a PUT call.
But some systems only handle GET calls - and Butler should still be able to notify them using webhooks.
The chosen solution is to offer full flexibility for outgoing webhooks and support both GET, PUT and POST calls.
Webhook notifications can be turned off all together with the Butler.webhookNotification.enable property in the config file.
If that property is true both task fail and abort webhooks are enabled.
If you don’t need any outgoing webhooks you should keep the Butler.webhookNotification.reloadTaskFailure.webhooks and Butler.webhookNotification.reloadTaskAborted.webhooks arrays empty.
There are also rate limiting properties that are used to ensure that webhooks are not sent too often.
Certificates and https
Outgoing webhooks can use http or https.
If https is used and the server being called uses a publicly trusted certificate, no additional configuration is needed.
If the server uses a self-signed certificate, the corresponding root CA certificate must be provided to Butler in order to avoid certificate validation errors.
Each webhook has its own certificate configuration, so Butler can be integrated with many systems, each using their own publicly verified or self-signed certificates - or just plain http without any certificates at all.
The certificate configuration is done in the Butler config file and looks like this for each webhook:
...cert:enable:true# Set to true to use a custom CA certificate when calling the webhookURLrejectUnauthorized:true# Set to false to ignore warnings/errors caused by self-signed certificates used on the webhooks server.certCA:/path/to/ca-certificate.pem # Path to the CA certificate file...
If ...cert.enable is set to true Butler will use the certificate specified in ...cert.certCA when calling the webhook.
If ...cert.rejectUnauthorized is set to false Butler will ignore warnings/errors caused by self-signed certificates being used on the webhook server.
Data included in outgoing webhooks
This information is included in all outgoing webhook calls:
Field
Description
event
Type of event, for example Qlik Sense reload failed.
hostName
Name of server where the event took place.
user
User directory/userId of user causing the event. For task failures this will be the user account used to do the reload. For aborts it will be the user stopping/aborting the task.
taskName
Task name
taskId
Task ID
appName
App name
appId
App ID
logTimeStamp
Timestamp entry in the Qlik Sense log files when the event took place.
logLevel
Log level used in the Qlik Sense log file in which the event was detected by the log appender.
executionId
Execution ID of the failed/aborted task.
logMessage
Message in Qlik Sense log file that triggered the event.
GET call
When doing GET calls all the data fields will be passed as search parameters in the URL.
For example, a failed task GET call to a remote URL could look like this:
The received/remote system can then unpack the URL parameters and use them as needed.
PUT and POST calls
PUT and POST calls work the same when it comes to Butler’s outgoing webhooks:
A stringified JSON is created based on the event’s data fields.
The string is sent in the POST/PUT call’s body.
The same event as above looks like this:
{"event":"Qlik Sense reload failed","hostName":"pro2-win1","user":"LAB\\goran","taskName":"Manually triggered reload of Test failing reloads 2","taskId":"dec2a02a-1680-44ef-8dc2-e2bfb180af87","appName":"Test failing reloads 2","appId":"e7af59a0-c243-480d-9571-08727551a66f","logTimeStamp":"2021-02-16 09:24:59,099","logLevel":"INFO","executionId":"14a81bf5-f81c-4047-b1a1-193b0920de28","logMessage":"Max retries reached"}
Settings in config file
---Butler:......# Settings for notifications and messages sent using outgoing webhookswebhookNotification:enable:falsereloadTaskFailure:rateLimit:300# Min seconds between outgoing webhook calls for a given taskID. Defaults to 5 minutes.webhooks:- description:'This outgoing webhook is used to...'# Informational onlywebhookURL:http://host.my.domain:port/some/path # Outgoing webhook that Butler will callhttpMethod:POST # GET/POST/PUTcert:enable:false# Set to true to use a custom CA certificate when calling the webhookURLrejectUnauthorized:true# Set to false to ignore warnings/errors caused by self-signed certificates used on the webhooks server.certCA:/path/to/ca-certificate.pem # Path to the CA certificate file- description:'This outgoing webhook is used to...'# Informational onlywebhookURL:http://host.my.domain:port/some/path # Outgoing webhook that Butler will callhttpMethod:PUT # GET/POST/PUTcert:enable:false# Set to true to use a custom CA certificate when calling the webhookURLrejectUnauthorized:true# Set to false to ignore warnings/errors caused by self-signed certificates used on the webhooks server.certCA:/path/to/ca-certificate.pem # Path to the CA certificate file- description:'This outgoing webhook is used to...'# Informational onlywebhookURL:http://host.my.domain:port/some/path # Outgoing webhook that Butler will callhttpMethod:GET # GET/POST/PUTcert:enable:false# Set to true to use a custom CA certificate when calling the webhookURLrejectUnauthorized:true# Set to false to ignore warnings/errors caused by self-signed certificates used on the webhooks server.certCA:/path/to/ca-certificate.pem # Path to the CA certificate filereloadTaskAborted:rateLimit:300# Min seconds between outgoing webhook calls for a given taskID. Defaults to 5 minutes.webhooks:- description:'This outgoing webhook is used to...'# Informational onlywebhookURL:http://host.my.domain:port/some/path # Outgoing webhook that Butler will callhttpMethod:PUT # GET/POST/PUTcert:enable:false# Set to true to use a custom CA certificate when calling the webhookURLrejectUnauthorized:true# Set to false to ignore warnings/errors caused by self-signed certificates used on the webhooks server.certCA:/path/to/ca-certificate.pem # Path to the CA certificate file- description:'This outgoing webhook is used to...'# Informational onlywebhookURL:http://host.my.domain:port/some/path # Outgoing webhook that Butler will callhttpMethod:POST # GET/POST/PUTcert:enable:false# Set to true to use a custom CA certificate when calling the webhookURLrejectUnauthorized:true# Set to false to ignore warnings/errors caused by self-signed certificates used on the webhooks server.certCA:/path/to/ca-certificate.pem # Path to the CA certificate file- description:'This outgoing webhook is used to...'# Informational onlywebhookURL:http://host.my.domain:port/some/path # Outgoing webhook that Butler will callhttpMethod:GET # GET/POST/PUTcert:enable:false# Set to true to use a custom CA certificate when calling the webhookURLrejectUnauthorized:true# Set to false to ignore warnings/errors caused by self-signed certificates used on the webhooks server.certCA:/path/to/ca-certificate.pem # Path to the CA certificate file......
2.3.6.2 - Reload alerts for Qlik Sense Cloud
Butler offers a lot of flexibility when it comes to alerts when reloads fai in Qlik Sense Cloud.
Learn how to set up the desired features, the alert layout, formatting and more.
Warning
In general the information related to an app that failed reloading will not be very sensitive.
It’s app name, owner id, tenant id etc.
If this information is considered sensitive in your organization, you should consider the security implications of sending this information to service like Butler via the public Internet. The traffic will be https encrypted, but even so, the information will be sent over the public Internet.
As always, make sure to follow your organization’s security guidelines. Think before you act.
Alert types
These alert types are available:
Reload task failure. Send alerts when app reloads fail.
Alert destinations and options
Alerts can be sent to these destinations, with different options available for each destination.
Each destination can be individually enabled/disabled in the config file.
Destination
App reload failure
Enable/disable alert per app
Alerts to app owners
Flexible formatting
Basic formatting
Comment
Email
β
β
β
β
Slack
β
β
β
β
MS Teams
β
β
β
β
How it works
Somehow Butler needs to be notified when a reload task in Qlik Sense Cloud fails.
The only way to do this is currently (2024 October) to use Qlik Cloud’s outgoing webhooks, and have them triggered when the app reload fails.
So, the outbound webhook should call some URL it can reach.
In practice this means a URL on the public Internet.
This could be a Butler provided endpoint, but exposing Butler to the public Internet is not a good idea from a security perspective.
There are various ways to solve this, each described below.
More options for brining Qlik Cloud events to Butler may be added in the future.
Option 1: Azure function forwarding event to MQTT
While this solution may be seen as a bit complex, it does offer some advantages:
No need to expose Butler to the public Internet.
The http-to-MQTT gateway is a minimal service that can be run as a serverless function in your cloud provider of choice, or on-premise in a de-militarized zone (DMZ). The point is that it’s a very small and simple service that both is easier to deploy and to secure, compared to a full Butler instance.
Not having complex services like Butler exposed to the public Internet is a good security practice.
The http-to-MQTT gateway can be used for other purposes too, such as sending MQTT messages to Butler when other events occur in your Qlik Cloud environment.
The http-to-MQTT gateway can be used to send MQTT messages to other systems too, not just Butler.
By exposing several https endpoints, The http-to-MQTT gateway can be used to send MQTT messages to Butler when events occur in other systems, not just Qlik Cloud.
By using a serverless function in a cloud provider, the solution scales well and can benefit from the cloud provider’s security features.
Low cost. The solution can even be run on a free tier in most cloud providers, and MQTT services are usually very cheap for the message volume in this scenario (one message per failed app reload).
Fast. Events typically reach Butler within a few seconds after they occur in Qlik Cloud.
Downsides include:
The solution is a bit complex to set up.
The solution requires the http-to-MQTT gateway to be up and running at all times.
A Internet connected MQTT broker is needed. There are several cloud based MQTT brokers available though.
The solution looks like this:
The webhook in Qlik Cloud is set up to call an Azure function when an app reload completes. The Azure function then sends an MQTT message to Butler.
The webhook is defined like this:
The webhook secret can be used in the gateway to verify that the webhook call is coming from an approved Qlik Cloud tenant.
Future options
Various solutions are possible, including:
Qlik Cloud supporting other notification mechanisms, such as sending MQTT messages when app reloads fail.
Qlik Cloud Application Automations supporting MQTT. The failed app reload could then be captured via an automation, and there forwarded to Butler via MQTT.
Using a 3rd party service that runs a webhook-to-MQTT gateway in the cloud.
If the basic assumption is that you want to expose as little attack surface on the Internet as possible, the solution will most likely involve some kind of intermediate service that can be reached by Qlik Cloud, and that can in turn asynchronously forward the event to Butler.
Setting up the http-to-MQTT gateway
An Azure Function App is used in this example, but the same concept can be used with other cloud providers, or on-premise.
In the example below the Azure function is written in Node.js.
Note that the code below is a basic example that should be extended before being used in a production environment:
Add proper error handling and logging
Add better security, using the Qlik Cloud webhook secret to verify that the incoming request is coming from an approved Qlik Cloud tenant.
The function does verify that the incoming request has a http header named x-myheader-foo1, but it does not check the value of that header. Room for improvement there.
All in all the function does work, it has been in test use for some months and should serve well as a starting point for your own implementation.
import{app,HttpRequest,HttpResponseInit,InvocationContext}from'@azure/functions';import{connectAsync}from'mqtt';exportasyncfunctionqscloudreload(request:HttpRequest,context:InvocationContext):Promise<HttpResponseInit>{context.log(`Http function processed request for url "${request.url}"`);context.log(`Request method: ${request.method}`);// Get query string parameters
constquery=Object.fromEntries(request.query.entries());context.log(`Request query:\n${JSON.stringify(query,null,2)}`);// Ensure there are no query string parameters
if(Object.keys(query).length>0){context.log('Too many query string parameters. Expected none.');return{status:400,body:'Invalid query string parameters'};}// -----------------------------------------------------
// Get headers
constheaders=Object.fromEntries(request.headers.entries());context.log(`Request headers:\n${JSON.stringify(headers,null,2)}`);// Ensure the correct headers are present
// The following headers are required:
// - accept-encoding: gzip
// - client-ip: <The IP address of the client making the request>
// - content-length: <The length of the request body>
// - content-type: application/json
// - host: <The host name of the function app>
// - qlik-signature: <The Qlik Sense Cloud signature of the request>
// - user-agent: Qlik Webhook
// - x-forwarded-proto: https
// - x-forwarded-tlsversion: 1.3
//
// Custom https headers (must also be present):
// - x-myheader-foo1: bar1
constrequiredHeaders=['accept-encoding','client-ip','content-length','content-type','host','qlik-signature','user-agent','x-forwarded-proto','x-forwarded-tlsversion','x-myheader-foo1'];for(constheaderofrequiredHeaders){if(!headers[header]){context.log(`Missing required header: ${header}`);return{status:400,body:`Missing required header`};}}// Make sure select headers contain correct values
// - accept-encoding: gzip
// - content-type: application/json
// - user-agent: Qlik Webhook
// - x-forwarded-proto: https
// - x-forwarded-tlsversion: 1.2 | 1.3
if(headers['accept-encoding']!=='gzip'){context.log(`Invalid header value for accept-encoding: ${headers['accept-encoding']}`);return{status:400,body:`Invalid header value for accept-encoding`};}if(headers['content-type']!=='application/json'){context.log(`Invalid header value for content-type: ${headers['content-type']}`);return{status:400,body:`Invalid header value for content-type`};}if(headers['user-agent']!=='Qlik Webhook'){context.log(`Invalid header value for user-agent: ${headers['user-agent']}`);return{status:400,body:`Invalid header value for user-agent`};}if(headers['x-forwarded-proto']!=='https'){context.log(`Invalid header value for x-forwarded-proto: ${headers['x-forwarded-proto']}`);return{status:400,body:`Invalid header value for x-forwarded-proto`};}if(headers['x-forwarded-tlsversion']!=='1.2'&&headers['x-forwarded-tlsversion']!=='1.3'){context.log(`Invalid header value for x-forwarded-tlsversion: ${headers['x-forwarded-tlsversion']}`);return{status:400,body:`Invalid header value for x-forwarded-tlsversion`};}// -----------------------------------------------------
// Get request body
letbody:any=JSON.parse(awaitrequest.text());letbodyString=JSON.stringify(body,null,2);context.log(`Request body:\n${bodyString}`);// Make sure the request body contains the expected properties
// The following properties are required:
// - cloudEventsVersion: 0.1
// - source: com.qlik/engine,
// - contentType: application/json,
// - eventId: b0f5c473-5dea-4d7e-a188-5e0b904cde33,
// - eventTime: 2024-07-27T13:57:27Z,
// - eventTypeVersion: 1.0.0,
// - eventType: com.qlik.v1.app.reload.finished,
// - extensions: <object with the following properties>
// - ownerId: <userID of the owner of the Qlik Sense resource that triggered the event>
// - tenantId: <tenantID of the Qlik Sense tenant that contains the Qlik Sense resource that triggered the event>
// - userId: <userID of the user that triggered the event>
// data: <object>
constrequiredProperties=['cloudEventsVersion','source','contentType','eventId','eventTime','eventTypeVersion','eventType','extensions','data'];for(constpropertyofrequiredProperties){if(!body[property]){context.log(`Missing required body property: ${property}`);return{status:400,body:`Missing required body property`};}}// Make sure the extensions object contains the expected properties
// The following properties are required:
// - ownerId: <userID of the owner of the Qlik Sense resource that triggered the event>
// - tenantId: <tenantID of the Qlik Sense tenant that contains the Qlik Sense resource that triggered the event>
// - userId: <userID of the user that triggered the event>
constextensions=body.extensions;constextensionsProperties=['ownerId','tenantId','userId'];for(constpropertyofextensionsProperties){if(!extensions[property]){context.log(`Missing required extensions property in request body: ${property}`);return{status:400,body:`Missing required extensions property`};}}// Make sure select properties contain correct values
// - cloudEventsVersion: 0.1
// - contentType: application/json
if(body.cloudEventsVersion!=='0.1'){context.log(`Invalid body value for cloudEventsVersion: ${body.cloudEventsVersion}`);return{status:400,body:`Invalid body value for cloudEventsVersion`};}if(body.contentType!=='application/json'){context.log(`Invalid body value for contentType: ${body.contentType}`);return{status:400,body:`Invalid body value for contentType`};}// -----------------------------------------------------
// Forward message to MQTT broker
constbrokerHost='hostname.of.mqtt.broker';constbrokerPort=8765;constmqttClient=awaitconnectAsync(`mqtts://${brokerHost}:${brokerPort}`,{username:'my-username',password:'my-password',});consttopic=`qscloud/app/reload/${body?.extensions?.tenantId}`;context.log(`Using MQTT topic: ${topic}`);context.log('MQTT client connected');mqttClient.publish(topic,bodyString,(err)=>{if(err){context.log(`Error publishing message: ${err}`);}});context.log('Message published');awaitmqttClient.endAsync();context.log('MQTT client disconnected');// Return a 200 response
return{status:200,// body: `Body received:\n${bodyString}`
body:`OK. Message received.`};};app.http('qscloudreload',{methods:['POST'],authLevel:'anonymous',handler:qscloudreload});
Customizing the alerts
The alerts can be customized in the same ways as for Qlik Sense client-managed. More info at links below.
2.3.6.2.1 - Qlik Cloud reload alerts sent as emails
Description of the various kinds of alert emails Butler can send when an app reload fails in Qlik Cloud.
What’s this?
Butler can send these alert emails:
When an app reload fails during execution.
See the Concepts section for additional details and sample alert emails.
Basic vs formatted email alerts
If you want Butler to send email alerts you must provide an email template file.
For some other alert destinations (Slack and Teams) Butler offers a “basic” option. A fixed format alert is then sent by Butler.
This is not the case for email alerts - there you must provide Butler with a template file.
Rate limiting and de-duplication
Butler has rate limiting feature to ensure alert recipients are not spammed with too many alert emails.
The rate limit is configured (in seconds) in the main config file in the Butler.qlikSenseCloud.event.mqtt.tenant.alert.emailNotification.reloadAppFailure.rateLimit setting in the config file.
Rate limiting is done based on app reload ID + email address.
Butler also has a de-duplication feature that ensure each email address that has qualified for an alert email only gets ONE email per alert, even if the email address (for example) appears as both an app owner and is specified via an app tag.
Butler can optionally send alert emails to the owner of apps that fail reloading.
Note
App owner notification email can only be sent to app owners that have an email stored in their Qlik Cloud user profile.
If there is no email available for an app owner, he/she will simply not receive any alert emails.
This feature is controlled by the config file properties Butler.qlikSenseCloud.event.mqtt.tenant.alert.emailNotification.reloadAppFailure.appOwnerAlert.enable.
If set to true the app owner will be added to the send list of alert emails, in addition to the recipients specied in Butler.qlikSenseCloud.event.mqtt.tenant.alert.emailNotification.reloadAppFailure.recipients.
The sections of the config file dealing with app owner notification emails looks like this:
appOwnerAlert:enable:false# Should app owner get notification email (assuming email address is available in Sense)?includeOwner:includeAll:true# true = Send notification to all app owners except those in exclude list# false = Send notification to app owners in the include listuser:# Array of app owner email addresses that should get notifications# - email: anna@somecompany.com# - email: joe@somecompany.comexcludeOwner:user:# - email: daniel@somecompany.com
It works like this:
If appOwnerAlert.enable is set to false no app owner emails will be sent. If it’s set to true the rules below apply.
If appOwnerAlert.includeOwner.includeAll is set to true all app owners will get notification emails when apps the own fail/are aborted…
… except those app owners listed in the appOwnerAlert.excludeOwner.user array.
That array thus provides a way to exclude some app owners (e.g. system accounts) to receive notifcation emails.
If appOwnerAlert.includeOwner.includeAll is set to false it’s still possible to add individual app owners to the appOwnerAlert.includeOwner.user array.
Those users will then receive notification emails for apps they own.
Send alerts only for some apps
Some apps may be more important than others.
I.e. some apps should result in alert emails when they fail reloading, but others not.
Butler controls which app reload failures cause email alerts by looking at a specific app tag.
If the config file setting Butler.qlikSenseCloud.event.mqtt.tenant.alert.emailNotification.reloadAppFailure.alertEnableByTag.enable is set to false, all failed app reloads will result in alert emails.
If that setting is true only some apps will cause alert emails when their reload fails:
If an app has the tag specified in Butler.qlikSenseCloud.event.mqtt.tenant.alert.emailNotification.reloadAppFailure.alertEnableByTag.tag, an email alert will be sent for that app if it fails reloading.
If an app does not have that tag set, no alert will be sent for that app.
Some configuration in Sense is needed to make this work:
Make changes to the config file. Specifically the settings mentioned above needs to be reviewed and (probably) updated.
In Qlik Cloud, tag the apps that should cause alert emails when they fail reloading.
Use the same tag as specified in the config file.
Looks like this in Qlik Sense Cloud:
How it works
The concept is the same for all alert types, see the this page for details.
Settings in config file
---Butler:......mqttConfig:enable:false# Should Qlik Sense events be forwarded as MQTT messages?......qlikSenseCloud:# MQTT settings for Qlik Sense Cloud integrationevent:mqttForward:# QS Cloud events forwarded to MQTT topics, which Butler will subscribe toenable:falsebroker:# Settings for MQTT broker to which QS Cloud events are forwardedhost:mqttbroker.company.comport:<port>username:<username>password:<password>topic:subscriptionRoot:qscloud/# # Topic that Butler will subscribe toappReload:qscloud/app/reload......qlikSenseCloud:# Settings for Qlik Sense Cloud integrationenable:falseevent:mqtt:# Which QS Cloud tenant should Butler receive events from, in the form of MQTT messages?tenant:id:tenant.region.qlikcloud.comtenantUrl:https://tenant.region.qlikcloud.comauthType:jwt # Authentication type used to connect to the tenant. Valid options are "jwt" auth:jwt:token:<JWT token> # JWT token used to authenticate Butler when connecting to the tenant# Qlik Sense Cloud related links used in notification messagesqlikSenseUrls:qmc:<URL to QMC in Qlik Sense Cloud>hub:<URL to Hub in Qlik Sense Cloud>comment:This is a comment describing the tenant and its settings# Informational onlyalert:......# Settings needed to send email notifications when for example reload tasks fail.# Reload failure notifications assume a log appender is configured in Sense AND that the UDP server in Butler is running.emailNotification:reloadAppFailure:enable:false# Enable/disable app reload failed notifications via emailalertEnableByTag:enable:falsetag:Butler - Send email if app reload failsappOwnerAlert:enable:false# Should app owner get notification email (assuming email address is available in Sense)?includeOwner:includeAll:true# true = Send notification to all app owners except those in exclude list# false = Send notification to app owners in the include listuser:# Array of app owner email addresses that should get notifications# - email: anna@somecompany.com# - email: joe@somecompany.comexcludeOwner:user:# - email: daniel@somecompany.comrateLimit:60# Min seconds between emails for a given taskID. Defaults to 5 minutes.headScriptLogLines:15tailScriptLogLines:25priority:high # high/normal/lowsubject: 'β Qlik Sense reload failed:"{{taskName}}"'bodyFileDirectory:/path/to//email_templateshtmlTemplateFile:failed-reload-qscloudfromAddress:Qlik Sense (no-reply) <qliksense-noreply@ptarmiganlabs.com>recipients:# - emma@somecompany.com# - patrick@somecompany.com......
Templates: Configuring email appearance
Alert emails use standard HTML formatting. Inline CSS can be used (if so desired) for fine tuning the visual look of the alert email.
Butler’s process for sending alert emails is
Figure out which email body template file should be used. This is determine by two set of fields in the main config file:
For reload failure emails these config file properties are used:
Butler.qlikSenseCloud.event.mqtt.tenant.alert.emailNotification.reloadAppFailure.bodyFileDirectory and Butler.qlikSenseCloud.event.mqtt.tenant.alert.emailNotification.reloadAppFailure.htmlTemplateFile. A .handlebars extension is assumed.
Email subjects are specified in the config property Butler.qlikSenseCloud.event.mqtt.tenant.alert.emailNotification.reloadAppFailure.subject.
Process the body template, replacing template fields with actual values.
Process the email subject template, replacing template fields with actual values.
Send the email.
A couple of sample template files are found in the src/config/email_templates directory of the GitHub repository.
Tip
You can use template fields in email subjects too!
Using custom links in templates
It is also possible to define custom links in the config file, and use them in email templates.
This is described here: Custom links in alerts.
Template fields reference
A complete list of template fields - including descriptions - is available in the Reference section.
2.3.6.2.2 - Reload alerts via Slack
Description of how app reload failed alerts can be sent as Slack messages.
What’s this?
Butler can send two kinds of alert messages via Slack:
A complete reference to the config file format is found here.
Basic vs formatted Slack alerts
Slack alerts come in two forms:
Customizable formatting using a template concept. A standard template that will fit most use cases is included with Butler. Using this option the first and last parts of the script log can be included in the message, making it possible to tell from the Slack message what caused the reload to fail.
You can also add buttons to the message that can be used to open any URL you want, or open the app that failed reloading.
A fixed, more basic format that is built into Butler. No template file needed, but also less detailed.
Which option to go for depends on whether you want just a notification that something went wrong, or if you want as much detail as possible in the Slack message. In most cases the customizable formatting is the best option.
Sample message with custom formatting
An “app reload failed” Slack message using the custom formatting option could look like this:
Here’s how to set this up:
Create an incoming webhook in Slack, take note of its URL (you will need it in step 2 below).
Edit the Slack section of the config file, i.e. the settings in Butler.qlikSenseCloud.event.mqtt.tenant.alert.slackNotification.reloadAppFailure.
The messageType property should be set to formatted.
The basicMsgTemplate property is not used with formatted messages and can thus be left empty,
Edit the template file if/as needed, the file is specified in Butler.qlikSenseCloud.event.mqtt.tenant.alert.slackNotification.reloadAppFailure.templateFile. It uses the Handlebars templating engine, to which Butler provides template fields with actual values.
Sample template files are included in the release Zip file, and are also available in the GitHub repository’s src/config/slack_templates directory.
Restart Butler if it’s already running.
Sample message with basic formatting
A “reload task failed” Slack message with basic formatting could look like this:
To set it up:
Create an incoming webhook in Slack if you don’t already have one, take note of its URL (you will need it in step 2 below).
Edit the Slack section of the config file, i.e. in Butler.qlikSenseCloud.event.mqtt.tenant.alert.slackNotification.reloadAppFailure.
The messageType property should be set to basic.
The basicMsgTemplate property is the message that will be sent via Slack. Template fields can be used.
Restart Butler if it’s already running.
Customizing Slack messages
When using the formatted Slack alerts you have full freedom to create the alert you need.
Behind the scenes Slack messages are constructed from blocks defined in a JSON object. Each block can then contain either plain text, Markdown, images, buttons etc.
The Slack documentation is the best place for learning how to customize messages.
When it comes to Butler, it uses the Handlebars templating engine to render the template files into Slack JSON objects that are then sent to Slack via their APIs.
A few things to keep in mind when creating custom Slack messages:
The handlebars syntax itself must be correct. If incorrect no Slack JSON object will be created. And no Slack messages sent.
The handlebars template must result in a JSON object that adheres to Slack’s API specifications.
If the JSON syntax is somehow invaid the Slack API will return errors and no messages sent. JSON can be pretty sensitive to details, there should for example not be any trailing commas in properly formatted JSON objects.
Block Kit Builder: Great sandbox wtih readily available examples of different message layouts, syntax and more. Note: You must be logged into your Slack account to use this tool.
Using custom links in templates
It is also possible to define custom links in the config file, and use them in Slack templates.
This is described here: Custom links in alerts.
How it works
The concept is the same for all alert types, see the this page for details.
Settings in config file
---Butler:......mqttConfig:enable:false# Should Qlik Sense events be forwarded as MQTT messages?......qlikSenseCloud:# MQTT settings for Qlik Sense Cloud integrationevent:mqttForward:# QS Cloud events forwarded to MQTT topics, which Butler will subscribe toenable:falsebroker:# Settings for MQTT broker to which QS Cloud events are forwardedhost:mqttbroker.company.comport:<port>username:<username>password:<password>topic:subscriptionRoot:qscloud/# # Topic that Butler will subscribe toappReload:qscloud/app/reload......qlikSenseCloud:# Settings for Qlik Sense Cloud integrationenable:falseevent:mqtt:# Which QS Cloud tenant should Butler receive events from, in the form of MQTT messages?tenant:id:tenant.region.qlikcloud.comtenantUrl:https://tenant.region.qlikcloud.comauthType:jwt # Authentication type used to connect to the tenant. Valid options are "jwt" auth:jwt:token:<JWT token> # JWT token used to authenticate Butler when connecting to the tenant# Qlik Sense Cloud related links used in notification messagesqlikSenseUrls:qmc:<URL to QMC in Qlik Sense Cloud>hub:<URL to Hub in Qlik Sense Cloud>comment:This is a comment describing the tenant and its settings# Informational onlyalert:......# Settings for notifications and messages sent to SlackslackNotification:reloadAppFailure:enable:falsealertEnableByTag:enable:falsetag:Butler - Send Slack alert if app reload failsbasicContentOnly:falsewebhookURL:<URL to Slack webhook>channel:sense-task-failure # Slack channel to which app reload failure notifications are sentmessageType:formatted # formatted / basic. Formatted means that template file below will be used to create the message.basicMsgTemplate: 'Qlik Sense Cloud app reload failed:"{{appName}}"' # Only needed if message type = basic
rateLimit: 60 # Min seconds between emails for a given appId/recipient combo. Defaults to 5 minutes.
headScriptLogLines: 10
tailScriptLogLines: 20
templateFile: /path/to/slack_templates/failed-reload-qscloud.handlebars
fromUser: Qlik Sense
iconEmoji: ':ghost:'......
2.3.6.2.3 - Reload alerts via Microsoft Teams
Description of how reload alerts can be sent as Microsoft Teams messages.
What’s this?
Butler can send two kinds of alert messages via Teams:
A complete reference to the config file format is found here.
Basic vs formatted Teams alerts
Teams alerts come in two forms:
Customizable formatting using a template concept. A standard template that will fit most use cases is included with Butler. With this option the first and last parts of the script log can be included in the message, allowing you to tell from the Teams message what caused the reload to fail.
You can also add buttons to the message that can be used to open any URL you want, or open the app that failed reloading.
A fixed, more basic format that is built into Butler. No template file needed, but also less detailed.
Which option to go for depends on whether you want just a notification that something went wrong, or if you want as much detail as possible in the Teams message. In most cases the customizable formatting is the best option.
Sample message with custom formatting
An “app reload failed” Teams message using the custom formatting option could look like this:
Here’s how to set it up:
Create a workflow in Teams, take note of its URL (you will need it in step 2 below). More information on how to create a Teams workflow in the Concepts section.
Edit the Teams section of the config file, i.e. the settings in Butler.qlikSenseCloud.event.mqtt.tenant.alert.teamsNotification.reloadAppFailure.
The messageType property should be set to formatted.
The basicMsgTemplate property is not used with formatted messages and can thus be left empty,
Edit the template file if/as needed, the file is specified in Butler.qlikSenseCloud.event.mqtt.tenant.alert.teamsNotification.reloadAppFailure.templateFile.It uses the Handlebars templating engine, to which Butler provides template fields with actual values.
Sample template files are included in the release Zip file, and are also available in the GitHub repository’s src/config/teams_templates directory.
Restart Butler if it’s already running.
Sample message with basic formatting
A “reload task failed” Teams message with basic formatting could look like this:
To set it up:
Create a workflow in Teams if you don’t already have one, take note of its URL (you will need it in step 2 below).
Edit the Teams section of the config file i.e. Butler.qlikSenseCloud.event.mqtt.tenant.alert.teamsNotification.reloadAppFailure.
The messageType property should be set to basic.
The basicMsgTemplate property is the message that will be sent via Teams. Template fields can be used.
Restart Butler if it’s already running.
Customizing Teams messages
When using the formatted Teams alerts you have full freedom to create the alert you need.
Behind the scenes Teams messages are constructed as “Adaptive Cards”, which is standardised JSON format that Teams understands.
More information on Adaptive Cards can be found here, here and here.
When it comes to Butler, it uses the Handlebars templating engine to render a template file into an adaptive card JSON object that is then sent to the workflow webhook.
A few things to keep in mind when creating custom Teams messages:
The handlebars syntax itself must be correct. If incorrect no Teams JSON object will be created. And no Teams message sent.
The handlebars template must result in a JSON object that adheres to Teams’s specifications for JSON payloads.
If the JSON syntax is somehow invaid the Teams API will return errors and no messages sent. JSON can be pretty sensitive to details, there should for example not be any trailing commas in properly formatted JSON objects.
Using custom links in templates
It is also possible to define custom links in the config file, and use them in Teams templates.
This is described here: Custom links in alerts.
How it works
The concept is the same for all alert types, see the this page for details.
Settings in config file
---Butler:......mqttConfig:enable:false# Should Qlik Sense events be forwarded as MQTT messages?......qlikSenseCloud:# MQTT settings for Qlik Sense Cloud integrationevent:mqttForward:# QS Cloud events forwarded to MQTT topics, which Butler will subscribe toenable:falsebroker:# Settings for MQTT broker to which QS Cloud events are forwardedhost:mqttbroker.company.comport:<port>username:<username>password:<password>topic:subscriptionRoot:qscloud/# # Topic that Butler will subscribe toappReload:qscloud/app/reload......qlikSenseCloud:# Settings for Qlik Sense Cloud integrationenable:falseevent:mqtt:# Which QS Cloud tenant should Butler receive events from, in the form of MQTT messages?tenant:id:tenant.region.qlikcloud.comtenantUrl:https://tenant.region.qlikcloud.comauthType:jwt # Authentication type used to connect to the tenant. Valid options are "jwt" auth:jwt:token:<JWT token> # JWT token used to authenticate Butler when connecting to the tenant# Qlik Sense Cloud related links used in notification messagesqlikSenseUrls:qmc:<URL to QMC in Qlik Sense Cloud>hub:<URL to Hub in Qlik Sense Cloud>comment:This is a comment describing the tenant and its settings# Informational onlyalert:# Settings for notifications and messages sent to MS TeamsteamsNotification:reloadAppFailure:enable:falsealertEnableByTag:enable:falsetag:Butler - Send Teams alert if app reload failsbasicContentOnly:falsewebhookURL:<URL to MS Teams webhook>messageType:formatted # formatted / basicbasicMsgTemplate: 'Qlik Sense Cloud app reload failed:"{{appName}}"' # Only needed if message type = basicrateLimit:15# Min seconds between emails for a given appId. Defaults to 5 minutes.headScriptLogLines:15tailScriptLogLines:15templateFile:/path/to/teams_templates/failed-reload-qscloud-workflow.handlebars......
2.3.7 - Reload script logs
Butler can detect, capture and store all script logs of reload tasks that failed.
This makes it much easier to find and analyse the script logs of faile reloads.
Works with both client-managed Qlik Sense and Qlik Sense Cloud.
What’s this?
The idea is to save the full script logs of failed reloads.
Having access to the full logs can sometimes be what’s needed to understand what caused the failure.
Log files from client-managed Qlik Sense are stored in one directory hierarchy, while logs from Qlik Sense Cloud are stored in another.
The files are store in separate directories for each date.
The file name of the script log consists of
Client-managed: <timestamp of the reload failure>_<app ID>_<task ID>.log
Qlik Sense Cloud: <timestamp of the reload failure>_<app ID>_<reload ID>
This feature relies on the same Qlik Sense log appenders that the reload alerts uses. Please see that page for an in-depth discussion on how log appenders work and how to set them up.
Warning
The log appenders that catch failed reloads in the Qlik Sense scheduler must be set up on all Qlik Sense servers where reloads are happening for this feature to reliably capture all failed reloads.
Qlik Sense Cloud
Storing script logs on disk is closely associated with sending alerts about failed reloads.
Those alerts (email, Slack, Teams) can include the first and last few lines of the script log, whereas the full log is stored on disk using the feature described on this page.
Butler listens to the Qlik Sense Cloud event stream and captures the script logs of failed reloads.
Settings in config file
---Butler:......# Store script logs of failed reloads on disk.# The script logs will be stored in daily directories under the specified main directory below# NOTE: Use an absolute path when running Butler as a standalone executable! scriptLog:storeOnDisk:clientManaged:reloadTaskFailure:enable:falselogDirectory:/path/to/scriptlogs/qseowqsCloud:appReloadFailure:enable:falselogDirectory:/path/to/scriptlogs/qscloud......
2.3.8 - Monitoring Windows services
Butler can monitor Windows services and alert if they are not running.
This is useful for monitoring services that are critical for Qlik Sense to function - or any other important service.
Messages can be sent when services stop or start, with message destinations such as Slack, Teams, email, New Relic, InfluxDB, webhooks and MQTT.
What’s this?
Qlik Sense uses Windows Services to run the Qlik Sense Engine, Qlik Sense Repository Service, Qlik Sense Scheduler Service and more.
If any of these services stop, Qlik Sense will not work.
Butler can monitor these services and alert if they are not running and when they start again.
This feature is only available when Butler is running on Windows, on other OSs a warning will be logged when Butler is starting and the feature will be disabled.
How it works
Butler will poll the Windows Service Control Manager (SCM) for the status of the services that are configured to be monitored.
The polling interval is configurable via the Butler.serviceMonitor.frequency setting, but defaults to 30 seconds.
The services to be monitored are listed in Butler.serviceMonitor.monitor section of the config file.
If firewalls etc allow it it is possible to monitor services on remote Windows machines as well.
Three pieces of information are needed for each service to be monitored:
The host name of the machine where the service is running (Butler.serviceMonitor.monitor.<host>).
This config entry is shared for all services monitored on the same host.
The name of the service (Butler.serviceMonitor.monitor.<services>.name).
This is the name of the service as it appears in the Windows Service Control Manager (SCM).
Right click on a service in the Windows Services app and select Properties, then find the “Service name” on the General tab.
A “friendly name” that can be anything (Butler.serviceMonitor.monitor.<services>.friendlyName). This is useful as the Windows service name are not always very descriptive.
The friendly name is used in the alert messages sent to the various alert destinations, including InfluxDB and New Relic.
Each alert destination can be enabled or disabled via the Butler.serviceMonitor.alertDestination.<destination>.enable setting.
Settings in config file
The configuration of each alert destination is done in the destinations’ own section of the config file, for example Butler.teamsNotification.serviceStopped, Butler.emailNotification.serviceStopped, Butler.emailNotification.serviceStarted etc.
Those settings are described in sub-pages of this page.
---Butler:......# Monitor Windows services.# This feature only works when Butler is running on Windows Server or desktop.# On other OSs service monitoring will be automatically disabled.serviceMonitor:enable:false# Main on/off switch for service monitoringfrequency:every 30 seconds # https://bunkat.github.io/later/parsers.htmlmonitor:- host:<hostname or IP> # Host name of Windows computer where services are runningservices:# List of services to monitor- name:postgresql-x64-12 # Posgress/repository dbfriendlyName:Repository DB- name:QlikSenseEngineServicefriendlyName:Engine- name:QlikSensePrintingServicefriendlyName:Printing- name:QlikSenseProxyServicefriendlyName:Proxy- name:QlikSenseRepositoryServicefriendlyName:Repository- name:QlikSenseSchedulerServicefriendlyName:Scheduler- name:QlikSenseServiceDispatcherfriendlyName:Service DispatcheralertDestination:# Control to thich destinations service related alerts are sentinfluxDb:# Send service alerts to InfluxDBenable:truenewRelic:# Send service alerts to New Relicenable:trueemail:# Send service alerts as emailsenable:truemqtt:# Send service alerts as MQTT messagesenable:trueteams:# Send service alerts as MS Teams messagesenable:trueslack:# Send service alerts as Slack messagesenable:truewebhook:# Send service alerts as outbound webhooks/http callsenable:true......
2.3.8.1 - Sending Windows service alerts as email
This page contains information on how to configure Butler to send email alerts when Windows services stop or start.
What’s this?
These config settings are specific to the email alert destination.
They are used in addition to the general Windows Service monitoring settings in Butler.serviceMonitor.
How it works
The sent emails are created from template files using the Handlebars templating engine.
The template files are located in the Butler.emailNotification.<alertType>.bodyFileDirectory directory, with the actual file name specified in Butler.emailNotification.<alertType>.htmlTemplateFile.
The template files can contain Handlebars expressions to insert values from the alert data.
The available values are:
Value
Description
{{host}}
The hostname of the server where the service is running
{{serviceStatus}}
The status of the service, e.g. RUNNING or STOPPED
{{servicePrevStatus}}
The previous status of the service, e.g. RUNNING or STOPPED
{{serviceName}}
The name of the service as defined in Windows
{{serviceDisplayName}}
The display name of the service as defined in Windows. Can sometimes be a bit more human readable than the serviceName.
{{serviceFriendlyName}}
The display name of the service as defined in the Butler config file. Used to give the service a good name when both serviceName and serviceDisplayName are unsuitable for use in for example Grafana dashboards.
{{serviceStartType}}
The startup mode of the service, e.g. Automatic or Manual
{{serviceExePath}}
The path to the executable of the service
Settings in config file
---Butler:......emailNotification:serviceStopped:rateLimit:30# Min seconds between emails for a given service. Defaults to 5 minutes.priority:high # high/normal/lowsubject:'β Windows service stopped on host {{host}}: "{{serviceDisplayName}}"'bodyFileDirectory:path/to/email_templates/email_templateshtmlTemplateFile:service-stoppedfromAdress:Qlik Sense (no-reply) <qliksense-noreply@mydomain.com>recipients:- <Email address 1>- <Email address 2>serviceStarted:rateLimit:30# Min seconds between emails for a given service. Defaults to 5 minutes.priority:high # high/normal/lowsubject:'β Windows service started on host {{host}}: "{{serviceDisplayName}}"'bodyFileDirectory:path/to/email_templates/email_templateshtmlTemplateFile:service-startedfromAdress:Qlik Sense (no-reply) <qliksense-noreply@mydomain.com>recipients:- <Email address 1>- <Email address 2>smtp:# Email server settings. See https://nodemailer.com/smtp/ for details on the meaning of these fields.host:<FQDN or IP or email server, e.g. smtp.gmail.com>port:<port on which SMTP server is listening>secure:true# true/falsetls:serverName:# If specified the serverName field will be used for TLS verification instead of the host field.ignoreTLS:falserequireTLS:truerejectUnauthorized:falseauth:enable:trueuser:<Username, email address etc>password:<your-secret-password>... ...
2.3.8.2 - Sending Windows service alerts to New Relic
This page contains information on how to configure Butler to send alerts messages to New Relic when Windows services stop or start.
What’s this?
These config settings are specific to the New Relic alert destination.
They are used in addition to the general Windows Service monitoring settings in Butler.serviceMonitor.
How it works
All settings are found in the Butler.incidetTool.newRelic.serviceMonitor section of the config file.
Butler can send two kinds of messages to New Relic: events and logs entries.
New Relic events and log entries are good at different things, and you can choose to send either or both.
In general, events are good for monitoring and alerting while log entries are good for logging and troubleshooting.
If in doubt, send both - that will give you the freedom to choose later which to use in the New Relic dashboards, alerts and incidents.
New Relic events
Windows service events will be sent to New Relic with the name of qs_serviceStateEvent.
The static attributes attached to events sents to New Relic events are the ones defined in the config file.
These can be used to identify which of potentially several Butler instances the message originated from, and to filter and group messages in New Relic.
The values of dynamic attributes are determined at runtime and can be enabled or disabled in the config file:
Dynamic attribute name in New Relic
Description
butler_serviceHost
The hostname of the server where the service is running
butler_serviceName
The name of the service as defined in Windows
butler_serviceDisplayName
The display name of the service as defined in Windows. Can sometimes be a bit more human readable than the serviceName.
butler_serviceStatus
The status of the service, e.g. RUNNING or STOPPED
New Relic log entries
Windows service log entries will be sent to New Relic with a log type of qs_serviceStateLog.
Static and dynamic attributes are handled in the same way as for events.
The raw data of a New Relic lg entry will look something like this:
Settings in config file
---Butler:......incidentTool:newRelic:serviceMonitor:destination:event:enable:falsesendToAccount:# Windows service events are sent to these New Relic accounts- First NR account- Second NR accountattribute:static:# Static attributes/dimensions to attach to events sent to New Relic.- name:event-specific-attributevalue:abc 123dynamic:serviceHost:true# Should host where service is running be sent to New Relic as attribute?serviceName:true# Should service name be sent to New Relic as attribute?serviceDisplayName:true# Should service display name be sent to New Relic as attribute?serviceState:true# Should service state be sent to New Relic as attribute?log:enable:falsesendToAccount:# Windows service log entries are sent to these New Relic accounts- First NR account- Second NR accountattribute:static:# Static attributes/dimensions to attach to events sent to New Relic.- name:log-specific-attributevalue:def 456dynamic:serviceHost:true# Should host where service is running be sent to New Relic as attribute?serviceName:true# Should service name be sent to New Relic as attribute?serviceDisplayName:true# Should service display name be sent to New Relic as attribute?serviceState:true# Should service state be sent to New Relic as attribute?monitorServiceState:# Control whih service states are sent to New Relicrunning:enable:truestopped:enable:truesharedSettings:rateLimit:5# Min seconds between events/logs sent to New Relic for a given host+service. Defaults to 5 minutes.header:# Custom http headers- name:X-My-Header # Examplevalue:Header value 2 # Exampleattribute:static:# Static attributes/dimensions to attach to events sent to New Relic.- name:service # Examplevalue:butler # Example- name:environment # Examplevalue:prod # Example... ...
2.3.8.3 - Storing Windows service alerts in InfluxDB
This page contains information on how to configure Butler to store alert information in InfluxDB when Windows services stop or start.
What’s this?
These config settings are specific to the InfluxDB alert destination.
They are used in addition to the general Windows Service monitoring settings in Butler.serviceMonitor.
How it works
There is no specific InfluxDB conmfiguration for Windows Service monitoring, so the general InfluxDB in Butler.influxDb settings are used.
This means that information about Windows service alerts are stored in the same InfluxDB database as other data points sent to InfluxDB from Butler (e.g. uptime metrics).
Settings in config file
---Butler:......# InfluxDB settingsinfluxDb:enable:false# Master switch for InfluxDB integration. If false, no data will be sent to InfluxDB.hostIP:<IP or host name> # Where is InfluxDB server located?hostPort:8086# InfluxDB portauth:enable:false# Does InfluxDB require login?username:user_joe password:joesecretdbName:butler # Name of database in InfluxDB to which Butler's data is writteninstanceTag:DEV # Tag that can be used to differentiate data from multiple Butler instances# Default retention policy that should be created in InfluxDB when Butler creates a new database there. # Any data older than retention policy threshold will be purged from InfluxDB.retentionPolicy:name:10dduration:10d ... ...
2.3.8.4 - Sending Windows service alerts to Slack
This page contains information on how to configure Butler to send alerts messages to Slack when Windows services stop or start.
What’s this?
These config settings are specific to the Slack alert destination.
They are used in addition to the general Windows Service monitoring settings in Butler.serviceMonitor.
How it works
All settings are found in the Butler.slackNotification.serviceStopped and Butler.slackNotification.serviceStarted sections of the config file.
Butler will send a Slack message to the channel specified in the config file when a Windows service stops or starts.
Similarly to how reload-failed Slack alerts work, Butler can send two types of Slack messages:
A simple message with just the name of the service that stopped or started. This will be the case if Butler.slackNotification.serviceStopped.messageType or Butler.slackNotification.serviceStarted.messageType is set to basic.
A more detailed and better formatted message with information about the service, the server it’s running on etc. This will be the case if Butler.slackNotification.serviceStopped.messageType or Butler.slackNotification.serviceStarted.messageType is set to formatted.
Rate limiting is controlled by the Butler.slackNotification.serviceStopped.rateLimit and Butler.slackNotification.serviceStarted.rateLimit settings.
Tip
The template used to create formatted Slack messages can be customized.
A formatted Slack message can look something like this:
Information availble in formatted Slack messages
Similar to how failed-reload email notifications work, the templating engine Handlebars is used to format the Slack messages.
The following information is available in formatted Slack messages:
Handlebars variable
Description
{{host}}
The hostname of the server where the service is running
{{serviceStatus}}
The status of the service, e.g. RUNNING or STOPPED
{{servicePrevStatus}}
The previous status of the service, e.g. RUNNING or STOPPED
{{serviceName}}
The name of the service as defined in Windows
{{serviceDisplayName}}
The display name of the service as defined in Windows. Can sometimes be a bit more human readable than the serviceName.
{{serviceFriendlyName}}
The friendly name of the service as defined in the config file.
{{serviceStartType}}
The start type of the service, e.g. AUTO_START or DEMAND_START
{{serviceExePath}}
The path to the service executable
Settings in config file
---Butler:......# Settings for notifications and messages sent to SlackslackNotification:serviceStopped:webhookURL:<web hook URL from Slack>channel:qliksense-service-alert # Slack channel to which Windows service stopped notifications are sentmessageType:formatted # formatted / basic. Formatted means that template file below will be used to create the message.basicMsgTemplate: 'Windows service stopped:"{{serviceName}}"onhost "{{host}}"' # Only needed if message type = basicrateLimit:30# Min seconds between messages for a given Windows service. Defaults to 5 minutes.templateFile:/path/to/slack/template/directory/service-stopped.handlebarsfromUser:Qlik SenseiconEmoji:':ghost:'serviceStarted:webhookURL:<web hook URL from Slack>channel:qliksense-service-alert # Slack channel to which Windows service stopped notifications are sentmessageType:formatted # formatted / basic. Formatted means that template file below will be used to create the message.basicMsgTemplate: 'Windows service started:"{{serviceName}}"onhost "{{host}}"' # Only needed if message type = basicrateLimit:30# Min seconds between messages for a given Windows service. Defaults to 5 minutes.templateFile:/path/to/slack/template/directory/service-started.handlebarsfromUser:Qlik SenseiconEmoji:':ghost:'... ...
2.3.8.5 - Sending Windows service alerts to Microsoft Teams
This page contains information on how to configure Butler to send alerts messages to Microsoft Teams when Windows services stop or start.
What’s this?
These config settings are specific to the Microsoft Teams alert destination.
They are used in addition to the general Windows Service monitoring settings in Butler.serviceMonitor.
How it works
All settings are found in the Butler.teamsNotification.serviceStopped and Butler.teamsNotification.serviceStarted sections of the config file.
Butler will send a Teams message to the channel associated with Butler.teamsNotification.<serviceStopped|servierStarted>.webhookRL in the config file when a Windows service stops or starts.
Similarly to how reload-failed Teams alerts work, Butler can send two types of Teams messages:
A simple message with just the name of the service that stopped or started. This will be the case if Butler.teamsNotification.serviceStopped.messageType or Butler.teamsNotification.serviceStarted.messageType is set to basic.
A more detailed and better formatted message with information about the service, the server it’s running on etc. This will be the case if Butler.teamsNotification.serviceStopped.messageType or Butler.teamsNotification.serviceStarted.messageType is set to formatted.
Rate limiting is controlled by the Butler.teamsNotification.serviceStopped.rateLimit and Butler.teamsNotification.serviceStarted.rateLimit settings.
Tip
The template used to create formatted Teams messages can be customized.
A formatted Teams message can look something like this:
Information availble in formatted Teams messages
Similar to how failed-reload email notifications work, the templating engine Handlebars is used to format the Teams messages.
The following information is available in formatted Teams messages:
Handlebars variable
Description
{{host}}
The hostname of the server where the service is running.
{{serviceStatus}}
The status of the service, e.g. RUNNING or STOPPED.
{{servicePrevStatus}}
The previous status of the service, e.g. RUNNING or STOPPED.
{{serviceName}}
The name of the service as defined in Windows.
{{serviceDisplayName}}
The display name of the service as defined in Windows. Can sometimes be a bit more human readable than the serviceName.
{{serviceFriendlyName}}
The friendly name of the service as defined in the config file.
{{serviceStartType}}
The start type of the service, e.g. AUTO_START or DEMAND_START.
{{serviceExePath}}
The path to the service executable.
Creating a MS Teams webhook
To create a webhook in MS Teams, follow the steps in the Concepts section.
Settings in config file
---Butler:......# Settings for notifications and messages sent to MS TeamsteamsNotification:serviceStopped:webhookURL:<web hook URL from MS Teams>messageType:formatted # formatted / basic. Formatted means that template file below will be used to create the message.basicMsgTemplate: 'Windows service stopped:"{{serviceName}}"onhost "{{host}}"' # Only needed if message type = basicrateLimit:30# Min seconds between messages for a given Windows service. Defaults to 5 minutes.templateFile:/path/to/teams/template/directory/service-stopped.handlebarsserviceStarted:webhookURL:<web hook URL from MS Teams>messageType:formatted # formatted / basic. Formatted means that template file below will be used to create the message.basicMsgTemplate: 'Windows service started:"{{serviceName}}"onhost "{{host}}"' # Only needed if message type = basicrateLimit:30# Min seconds between messages for a given Windows service. Defaults to 5 minutes.templateFile:/path/to/teams/template/directory/service-started.handlebars... ...
2.3.8.6 - Sending Windows service alerts as MQTT messages
This page contains information on how to configure Butler to send alerts as MQTT messages when Windows services stop or start.
What’s this?
These config settings are specific to the MQTT alert destination.
They are used in addition to the general Windows Service monitoring settings in Butler.serviceMonitor.
How it works
All settings are found in the Butler.mqttConfig section of the config file.
Butler will send two kinds of MQTT messages:
A state message indicating that a service has changed its state, for example from RUNNING to STOPPED.
When a service stops or starts, Butler will send a message to the topic defined in Butler.mqttConfig.serviceStoppedTopic, with /<hostname>/<serviceName> appended to the topic.
The payload will be a JSON with information about the service (name, display name, current state, previous state, dependencies, EXE path etc.).)
When a service starts the same thing happens, but the base topic used is defined in Butler.mqttConfig.serviceStartedTopic.
A message containing the current state of a service. These messages are sent when Butler starts up and when the state of a service changes.
The base MQTT topic for these messages are defined in the Butler.mqttConfig.serviceStateTopic setting. To this topic, Butler will append /<hostname>/<serviceName> before sending the message.
These messages are sent every time Butler checks the status of the Windows services, i.e. every Butler.serviceMonitor.frequency seconds.
The MQTT message will be sent as a JSON with information about the service (name, display name, current state, dependencies, EXE path etc.).
A few MQTT message can look like this when viewed in MQTT Explorer:
Settings in config file
---Butler:......mqttConfig:enable:false# Should Qlik Sense events be forwarded as MQTT messages?brokerHost:<FQDN or IP of MQTT server>brokerPort:1883serviceRunningTopic:qliksense/service_runningserviceStoppedTopic:qliksense/service_stoppedserviceStatusTopic:qliksense/service_status ... ...
2.3.8.7 - Sending Windows service alerts as outgoing webhooks (=http messages)
This page contains information on how to configure Butler to send alerts as outbound http calls, also known as “outbound webhooks”.
What’s this?
These config settings are specific to the outbound webhook alert destination.
They are used in addition to the general Windows Service monitoring settings in Butler.serviceMonitor.
How it works
All settings are found in the Butler.webhookNotification section of the config file.
Butler can send three kinds of http messages: POST, PUT and GET.
Some services only support one/some of these, so you need to check the documentation for the service you want to send the message to.
It is possible to define any number of webhook, and each destination can have its own settings such as http method and URL.
It is for example possible to send POST messages to different URLs if needed.
The rate limit defined in Butler.webhookNotification.rateLimit is calculated against each state change of the monitored Windows service.
There is no check with respect to rate limits how manu URLs are defined (and thus outbound http messages are sent).
Payload of outbound http calls
The same webhooks/URLs are used for both Windows service start and stop events.
The defails of the Windows service events is sent in the payload of the http message - exactly how depends on the http method used.
POST
The payload is sent as JSON in the body of the http message.
Here Node-RED is used to receive the http message and display it in a debug window:
PUT
The message payload is sent in the body, exactly as for POST messages.
The fields are the same as for POST and PUT messages, except that the field names are in lower case.
Settings in config file
---Butler:......# Settings for notifications and messages sent using outgoing webhookswebhookNotification:enable:falseserviceMonitor:rateLimit:15# Min seconds between outgoing webhook calls, per Windows service that is monitored. Defaults to 5 minutes.webhooks:- description:'This outgoing webhook is used to...'webhookURL:http://host.my.domain:port/some/path # outgoing webhook that Butler will callhttpMethod:POST # GET/POST/PUT. Note that the body and URL query parameters differs depending on which method is used- description:'This outgoing webhook is used to...'webhookURL:http://host.my.domain:port/some/path # outgoing webhook that Butler will callhttpMethod:PUT # GET/POST/PUT. Note that the body and URL query parameters differs depending on which method is used- description:'This outgoing webhook is used to...'webhookURL:http://host.my.domain:port/some/path # outgoing webhook that Butler will callhttpMethod:GET # GET/POST/PUT. Note that the body and URL query parameters differs depending on which method is used......
2.3.9 - Qlik Sense server version
Butler can monitor the server version of the client-managed Qlik Sense environment that Butler is configured to connect to.
Check server version at regular intervals.
Save version to InfluxDB.
Makes it easy to keep track of versions running on different Qlik Sense environments, for example PROD, TEST and DEV.
What’s this?
As with most software, client-mananged Qlik Senwse is updated regularly.
Butler can monitor the server version of the Qlik Sense environment that Butler is connected to and store this information in InfluxDB.
Having this information in InflixDB makes it easy to visualize it in a Grafana dashboard, or similar tool.
If you are running multiple Qlik Sense environments, for example PROD, TEST and DEV, you probably have one Butler instance running for each environment.
By storing the server version in InfluxDB, you can easily keep track of which Sense version is running on which environment.
How it works
Butler will periodically poll the Qlik Sense server for information about the server version.
The retrieved information is logged to the log file and can also optionally be stored in InfluxDB.
It is possible to add additional tags to the data sent to InfluxDB, for example to differentiate between PROD, TEST and DEV environments, to make later visualizations easier and richer.
How often to check the server version
The frequency of the server version check is configurable in the Butler.qlikSenseVersion.versionMonitor.frequency setting.
It uses the later.js syntax, for example every 24 hours or every 14 days.
Which InfluxDB database is used?
The data sent to InfluxDB is stored in the database specified in the Butler.influxDb setting.
Settings in config file
---Butler:......# Settings for monitoring Qlik Sense version info# Version info is retrieved from the hostname:9032/v1/systeminfo endpoint in Qlik SenseqlikSenseVersion:versionMonitor:enable:false# Should Qlik Sense version info be retrieved?frequency:every 24 hours # https://bunkat.github.io/later/parsers.html#texthost:<FQDN or IP of Qlik Sense central node> rejectUnauthorized:false# Set to false to ignore warnings/errors caused by Qlik Sense's self-signed certificates.destination:influxDb:# Store version data in InfluxDB.# If enabled, version info will be stored as measurements in InfluxDB.enable:falsetag:static:# Static attributes/tags to attach to the data sent to InflixDB- name:foovalue:bar......
2.3.10 - Qlik Sense server license
Butler can monitor the Qlik Sense server license that is used to run client-managed Qlik Sense (=Qlik Sense Enterrise on Windows).
Check license expiration date and alert a configurable number of days before expiration.
Send license status and expiration alerts to InfluxDB, webhooks and MQTT.
What’s this?
If the Qlik Sense server license expires, the Qlik Sense environment will go into a disabled state and users will not be able to access Sense.
Butler can monitor the Qlik Sense server license and alert if the license is about to expire.
How it works
Butler will periodically poll the Qlik Sense server for information about the Qlik Sense server license.
The retrieved information can be stored in/sent to zero or more of InfluxDB, webhooks and MQTT.
If the license is about to expire, Butler will send an alert to the configured alert destinations.
The alert will be sent a configurable number of days before the license expires, giving you time to renew the license.
The alert can also be stored in InfluxDB and/or sent to webhooks and MQTT.
How often to check the license
The frequency of the license check is configurable in the Butler.qlikSenseLicense.serverLicenseMonitor.frequency setting.
It uses the later.js syntax, for example every 24 hours or every 14 days.
What’s sendRecurring?
For each destination, you can configure if Butler should send the license status to the destination every time the license is checked.
This is useful if you want to keep track of the license status over time, for example in a Grafana dashboard.
What’s sendAlert?
For each destination, you can configure if Butler should send an alert if the license is about to expire, i.e. if the number of days left on the license is below the threshold specified in the Butler.qlikSenseLicense.serverLicenseMonitor.alert.thresholdDays setting.
This is useful if you want to be alerted (repeatedly) if the license is about to expire and possibly also store the alerts in InfluxDB.
Which InfluxDB database is used?
The data sent to InfluxDB is stored in the database specified in the Butler.influxDb setting.
Settings in config file
---Butler:......# Settings for monitoring Qlik Sense licensesqlikSenseLicense:serverLicenseMonitor:enable:falsefrequency:every 24 hours # https://bunkat.github.io/later/parsers.html#textalert:# Alert if the number of days left on the license is below the threshold# License expiry alerts on a global level are enabled here, then configured on # a per-destination basis elsewhere in this config file.thresholdDays:60destination:influxDb:# Store license data in InfluxDBenable:falsetag:static:# Static attributes/tags to attach to the data sent to InflixDB- name:foovalue:barmqtt:enable:falsesendRecurring:# Send license data to the MQTT broker at the frequency specified aboveenable:truesendAlert:# Send an MQTT alert if the number of days left on the license is below the thresholdenable:truewebhook:enable:falsesendRecurring:# Send license data to webhook(s) at the frequency specified aboveenable:truesendAlert:# Send alert to webhook(s) if the number of days left on the license is below # the threshold or the license has already expiredenable:true......
2.3.11 - Qlik Sense access licenses
Butler can monitor Qlik Sense user access licenses.
High level metrics per user license type (professional, analyzer etc) are gathered and stored in your database of choice (at the time of writing, InfluxDB is supported).
User licenses can be released automatically after a certain period of inactivity, allowing them to be used by other users.
What’s this?
It’s important to keep track of how Qlik Sense end user licenses are used.
If your Sense environment runs out of licenses, users without a license - but entitled to one - will not be able to access Sense.
By monitoring license usage you can make sure that you have enough licenses available, and get an early warning if you’re about to run out.
New licenses can then be ordered and installed before the current ones run out.
Additionally, some Sense users might only use Sense sporadically.
For example, a user might only use Sense during certain times of the year.
In such cases it’s a waste of resources to keep the license assigned to the user when it’s not being used.
Butler can be configured to periodically release Professional and Analyzer user licenses that have been inactive for a certain period of time.
How it works
Butler periodically polls the Qlik Sense Repository Service (QRS) for information about user licenses and store this information in the database specified in the Butler config file.
Similarly, Butler will periodically release Professional and/or Analyzer user licenses that have been inactive for a certain (configurable) period of time.
Monitoring Qlik Sense license usage
The config file settings below will (if enabled):
Every 6 hours, poll Qlik Sense for information about user licenses.
Store this information in InfluxDB and add a tag foo with the value bar to the data sent to InfluxDB.
Adapt as needed to your environment.
Releasing inactive user licenses
The config file settings below will (if enabled):
Every 24 hours, release Professional and Analyzer access licenses that have been inactive for 30 days or more.
Never release access licenses for…
users INTERNAL\sa_repository, INTERNAL\sa_api and USERDIR\qs_admin_account.
users tagged with License do not release or some other tag.
users with custom property LicenseManage set to do-not-release.
users in user directories INTERNAL and ADMIN.
Disregard users’ inactive, blocked and removed externally status when deciding whether to release their access licenses.
Store information about released licenses in InfluxDB and add a tag foo with the value bar to the data sent to InfluxDB.
Which InfluxDB database is used?
The data sent to InfluxDB is stored in the database specified in the Butler.influxDb setting.
Settings in config file
---Butler:......# Settings for monitoring Qlik Sense licensesqlikSenseLicense:......licenseMonitor:# Monitor Qlik Sense accesds license usageenable:falsefrequency:every 6 hours # https://bunkat.github.io/later/parsers.html#textdestination:influxDb:# Store license data in InfluxDBenable:falsetag:static:# Static attributes/tags to attach to the data sent to InflixDB- name:foovalue:barlicenseRelease:# Release unused Qlik Sense access licensesenable:false# true/false. If true, Butler will release unused licenses according to settings belowdryRun:true# true/false. If true, Butler will not actually release any licenses, just log what it would have done. frequency:every 24 hours # https://bunkat.github.io/later/parsers.html#textneverRelease:# Various ways of defining which users should never have their licenses releaseduser:# Users who should never have their licenses released- userDir:'INTERNAL'userId:'sa_repository'- userDir:'INTERNAL'userId:'sa_api'- userDir:'USERDIR'userId:'qs_admin_account'tag:# Users with these tags will never have their licenses released- License do not release- some other tagcustomProperty:# Users with these custom properties will never have their licenses released- name:LicenseManagevalue:do-not-releaseuserDirectory:# List of user directories whose users should never have their licenses released- INTERNAL- ADMINinactive:Ignore # Ignore/Yes/No. The value is case insensitive# No = Don't release licenses for users marked as "Inactive=No" in the QMC# Yes = Don't release licenses for users marked as "Inactive=Yes" in the QMC # Ignore = Disregard this settingblocked:Ignore # Ignore/Yes/No, No = Don't release licenses for users marked as "Blocked=No" in the QMCremovedExternally:ignore # Ignore/Yes/No, No = Don't release licenses for users marked as "Removed externally=No" in the QMClicenseType:# License types to monitor and releaseanalyzer:enable:true# Monitor and release Analyzer licensesreleaseThresholdDays:30# Number of days a license can be unused before it is releasedprofessional:enable:true# Monitor and release Professional licensesreleaseThresholdDays:30# Number of days a license can be unused before it is releaseddestination:influxDb:# Store info about released licenses in InfluxDBenable:falsetag:static:# Static attributes/tags to attach to the data sent to InflixDB- name:foovalue:bar......
2.3.12 - Configuring the Butler scheduler
Butler’s scheduler complements the Qlik Sense built-in scheduler with more flexible triggers and a devops friendly API/file format for storing scheduling data.
What’s this?
Some scheduling scenarios are difficult to achieve with the standard Qlik Sense scheduler. Butler attempts to solve this by offering a cron-based scheduler that can start Sense tasks according to schedule.
How it works
Butler’s scheduler can be used both via the REST API and by directly editing the scheduler config file.
Both options have their merits and use cases, the latter one can for example be useful if the scheduling file is kept on a Git server and copied to the Butler environment by means of some continuous delivery (CD) tool. The API can be useful when other systems need to change when Sense reloads take place, or to change the schedules from within Sense load scripts.
All schedules are stored in a YAML file. The location and name of the file is controlled by the config file property Butler.scheduler.configFile.
The Butler GitHub repository has a sample schedule file in the config directory, next to the main YAML config file:
It’s important to understand when schedules are stored to and loaded from disk:
The schedule file is read from disk when Butler starts.
When schedules are added, changed or deleted using the APIs, the set of schedules currently in Butler’s memory will be written to the schedule YAML file on disk.
Schedule file format
The schedule file contains an array of zero or more schedule entries.
The cron pattern in the cronSchedule property can be either 6 positions (left-most character is seconds) or 5 positions (left-most character is minutes).
qlikSenseTaskId is the id of the task to be started. The Task view in the QMC is useful for getting these IDs.
The name propery is for reference only. There may in theory be multiple schedule entries with the same (probably not a good idea though).
The id property must be unique. If a schedule is created using the API, the schedule id will be a GUID - but any unique string can be used.
startupState determines whether the schedule will be started or remain stopped when Butler starts.
lastKnownState is the the schedule’s last known state (running/stopped) known to Butler at the time when the schedule file was written to disk.
tags are purely are way to categorise schedules. Not used by Butler in any way, nor are they related to Qlik Sense tags in any way.
A 6 postition schedule that starts a task every 30 seconds can look like this:
A full description of the scheduler and its file format is available in the Reference docs section.
Settings in config file
---Butler:......# Scheduler for Qlik Sense tasksscheduler:enable:false# Should Butler's reload task scheduler be started?configfile:config/schedule.yaml # Path to file containing task start schedules......
2.3.13 - Configuring the key-value store
Butler contains a key-value store that is accessible via the REST API.
What’s this?
The key-value has several use cases:
Pass parameters between apps in a reload chain
Share state or other data between app reloads
Share state between extensions, mashups or other web apps.
Store data with a time-to-live property. Can be used to create timeouts in app reload chains.,
How it works
The data in the key-value store is not persisted to disk, which means that key-value data will be lost if Butler is restarted.
This behaviour could possibly be changed if there is a need, please open a GitHub ticket if key-value persistence is of interest.
Key-value data is manipulated using Butler’s REST API.
The Reference docs section has more information about the key-value store.
Settings in config file
---Butler:......# Key-value storekeyValueStore:enable:false# Should Butler's key-value store be enabled?maxKeysPerNamespace:1000# Max keys that can be stored per namespace. Defaults to 1000 if not specified in this file.......
2.3.14 - Configuring file system access via REST API
Butler contains REST API endpoints for moving, copying and deleting files.
What’s this?
For (good) security reasons Qlik Sense does not allow direct access to the file system.
In QlikView this was possible, but also resulted in risks and potential attack vectors for poorly written or even malicious QlikView apps.
Still, from time to time you need to delete old QVDs, move config files from an inbox directory to a staging ditto etc. Butler solves this by allowing file copy/move/delete operations between pre-defined directories.
By using the these APIs you can do file system operations from within Sense load scripts.
How it works
There are three supported file system operations: copy, move and delete:
For copy and move operations you specify which source and destination directories are allowed.
For delete operations you specify which directories file delete operations are allowed in.
Wilcards are supported.
Butler will try to clean up paths when loading them from the config file. See below for example.
As the config file is only read when Butler starts, you must restart Butler in order for any config changes to take effect.
Settings in config file
---Butler:......# List of directories between which file copying via the REST API can be done.# Butler will try to clean up messy paths like this one, which resolves to /Users/goran/butler-test-dir1# How? First you have /Users/goran/butler-test-dir1//abc which cleans up to /Users/goran/butler-test-dir1/abc, # then up one level (..).fileCopyApprovedDirectories:- fromDirectory:/Users/goran/butler-test-dir1//abc//..toDirectory:/Users/goran/butler-test-dir2- fromDirectory:/Users/goran/butler-test-dir2toDirectory:/Users/goran/butler-test-dir1- fromDirectory:/from/some/directory2toDirectory:/to/some/directory2# List of directories between which file moves via the REST API can be done.fileMoveApprovedDirectories:- fromDirectory:/Users/goran/butler-test-dir1//abc//..toDirectory:/Users/goran/butler-test-dir2- fromDirectory:/Users/goran/butler-test-dir2toDirectory:/Users/goran/butler-test-dir1- fromDirectory:/from/some/directory2toDirectory:/to/some/directory2# List of directories in which file deletes via the REST API can be done.fileDeleteApprovedDirectories:- /Users/goran/butler-test-dir1- /Users/goran/butler-test-dir1//abc//..- /Users/goran/butler-test-dir2......
2.3.15 - Incident management tools
There are various enterprise grade tools for handling IT incidents.
Butler can integrate with such tools, for example forwarding information about failed reloads.
Below you find instuctions for configuring the currently supported incident management tools.
2.3.15.1 - New Relic
New Relic is an enterprise grade observability solution in a SaaS package.
They offer a uniform approach to dealing with metrics, logs and events - including a basic but working alert management feature. If more advanced alert management is needed New Relic offers out-of-the-box integrations with tools like PagerDuty, ServiceNow, Jira, VictorOps and many other services.
The service is easy to get started with and has a generous free tier that works very well for testing Butler alerts. New Relic is a great choice as it handles both reload failure alerts for the Butler tool as well as both server and Sense specific operational metrics (CPU load, available memory, number of currently connected users etc) from Butler SOS.
New Relic exposes APIs through which data such as log entries as well as generic events and metrics can be sent to New Relic.
These logs, metrics and events are stored in New Relic’s databases for a configurable retention period.
Rules and queries against this data are used to create monitoring dashboards and notifications when reload tasks fail or are aborted.
The retention period of New Relic’s free tier is usually more than enough for Butler’s use cases, but their paid product versions offers even longer retention periods if/when needed.
To use Butler with New Relic you must
Create a New Relic account. The free/trial account is quite generous and will easily get you started.
Create an API key with insert permissions. See New Relic docs how to do this.
Configure the Butler config file.
More info about the New Relic event API that is used to send alerts can be found in New Relic’s API docs.
Rate limiting
If a reload task is set to run very frequently but fails every time, this will result in a lot of log entries and events sent to New Relic.
If New Relic alerts are configured to be sent for each reload failure event, there will be lots of alerts.
To handle this scenario Butler offers rate limiting for events sent to New Relic.
The Butler.incidentTool.newRelic.reloadTaskFailure.sharedSettings.rateLimit setting controls how often (seconds) reload-failed events will be sent to New Relic, at most.
A similar setting exists for aborted reloads.
Data sent to New Relic
Failed and aborted reloads
Butler can be configured to send neither, either or both of two different data sets to New Relic:
Failed reloads can be sent to New Relic as events.
A New Relic event has a basic set of event attritbutes associated with it. Examples are task name, task ID, app name and app ID. These attributes are always sent to New Relic.
Failed reloads can also be sent to New Relic as log entries.
Log entries are more versatile than events and can contain any text in the log message. Butler uses the log message to pass along the last x rows (x=configurable number) from the script log to New Relic. Having the script log from the failed reload available in New Relic makes it possible to see where the reload script failed and possible even what caused the failure.
Aborted reloads can be configured in exactly the same way as failed reloads, described above.
New Relic events
The following data is sent as New Relic events when a reload task fails or is aborted:
All http headers defined in the Butler config file.
All shared, static attributes defined in the Butler config file.
All event specific, static attributes defined in the Butler config file.
All tags for the Sense app that was reloaded (can be turned on/off in Butler config file).
All tags for the Sense reload task that was reloaded (can be turned on/off in Butler config file).
Butler version the event originated from. This is useful to have in New Relic as it makes it possible to easily show in a dashboard what Butler version is used and whether an update is possible/needed.
Event related data
Event type. Either qs_reloadTaskFailedEvent or qs_reloadTaskAbortedEvent.
Timestamp when the event took place.
Host where the reload task was executing.
User directory and ID for user which was doing the reloade. This will be the Sense service account in most cases.
Reload task name.
Reload task ID.
App name.
App ID.
Timestamp for this event in Sense log files.
Log level for this event in Sense log files.
Sense execution ID for this event.
Description of the event, as found in the Sense log files.
New Relic log entries
If Butler is configured to forward failed/aborted reload tasks to New Relic as log entries, the follow info is sent to New Relic:
All information sent for events (see above), but with log specific static attributes rather than event specific ditto.
The various states the reload task went through before failing, including timestamps when each state started.
The last x lines from the reload script log. x is configurable in the Butler.incidentTool.newRelic.reloadTaskFailure.destination.log.tailScriptLogLines setting.
The host name of the Sense node where the reload took place
Timestamp (in several different formats) when the reload started
Timestamp (in several different formats) when the reload failed
Duration of the reload task
Result code of the reload task
Result text of the reload task
Total size of complete script log (number of characters).
Number of lines included in the reload script log sent to New Relic
Monitoring Windows services
Butler can be configured to send Windows service start/stop events to New Relic as New Relic events and/or log entries.
The most common scenario is to send metrics and events to a single New Relic account.
There are however scenarios when sending data to multimple accounts can be of interest.
Workaround for lack of dashboard level access control
There is currently no access control on dashboard level in New Relic. This means that a user with read-only access to a New Relic account can access all dashboards in that account.
Let’s assume
There are 3 separate Sense environments (DEV, TEST, PROD) that should be monitored for failed reload alerts.
Different teams are responsible for the different Sense environments.
Each team should only have access to New Relic dashboards containing data from their Sense environment.
A central operations team should have dashoards containing data from all three environments.
A solution is then to create separate New Relic accounts for each team, plus one account for the central operations team.
Deploy separate Butler instances for DEV, TEST and PROD, and configure each to send data to both the central New Relic account and the separate DEV, TEST or PROD accounts.
Control which New Relic accounts to send data to
The Butler.thirdPartyToolsCredentials.newRelic section in the Butler config file defines which New Relic accounts metrics and events can be sent to:
Butler:......thirdPartyToolsCredentials:newRelic:# Array of New Relic accounts/insert keys. Any data sent to New Relic will be sent to both accounts. - accountName:First NR accountinsertApiKey:<API key 1 (with insert permissions) from New Relic> accountId:<New Relic account ID 1>- accountName:Second NR account