diff --git a/src/butler.js b/src/butler.js index 51d345c7..fc14190e 100644 --- a/src/butler.js +++ b/src/butler.js @@ -29,7 +29,9 @@ const start = async () => { globals.logger.verbose(`START: Globals init done: ${globals.initialised}`); const setupServiceMonitorTimer = (await import('./lib/service_monitor.js')).default; - const { setupQlikSenseLicenseMonitor, setupQlikSenseLicenseRelease } = await import('./lib/qliksense_license.js'); + const { setupQlikSenseAccessLicenseMonitor, setupQlikSenseLicenseRelease, setupQlikSenseServerLicenseMonitor } = await import( + './lib/qliksense_license.js' + ); const { setupQlikSenseVersionMonitor } = await import('./lib/qliksense_version.js'); // The build function creates a new instance of the App class and returns it. @@ -207,15 +209,23 @@ const start = async () => { setupQlikSenseVersionMonitor(globals.config, globals.logger); } - // Set up Qlik Sense license monitoring, if enabled in the config file + // Set up Qlik Sense server license monitoring, if enabled in the config file + if ( + globals.config.has('Butler.qlikSenseLicense.serverLicenseMonitor.enable') && + globals.config.get('Butler.qlikSenseLicense.serverLicenseMonitor.enable') === true + ) { + setupQlikSenseServerLicenseMonitor(globals.config, globals.logger); + } + + // Set up Qlik Sense access license monitoring, if enabled in the config file if ( globals.config.has('Butler.qlikSenseLicense.licenseMonitor.enable') && globals.config.get('Butler.qlikSenseLicense.licenseMonitor.enable') === true ) { - setupQlikSenseLicenseMonitor(globals.config, globals.logger); + setupQlikSenseAccessLicenseMonitor(globals.config, globals.logger); } - // Set up Qlik Sense license release, if enabled in the config file + // Set up Qlik Sense access license release, if enabled in the config file // Enable only if at least one license type is enabled for automatic release if ( globals.config.has('Butler.qlikSenseLicense.licenseRelease.enable') && diff --git a/src/config/config-gen-api-docs.yaml b/src/config/config-gen-api-docs.yaml index da95ade1..fc6896a5 100644 --- a/src/config/config-gen-api-docs.yaml +++ b/src/config/config-gen-api-docs.yaml @@ -148,6 +148,20 @@ Butler: # Settings for monitoring Qlik Sense licenses qlikSenseLicense: + serverLicenseMonitor: + enable: true + frequency: every 24 hours # https://bunkat.github.io/later/parsers.html#text + alert: # 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: 60 + destination: + influxDb: # Store license data in InfluxDB + enable: true + tag: + static: # Static attributes/tags to attach to the data sent to InflixDB + # - name: foo + # value: bar licenseMonitor: enable: false frequency: every 6 hours # https://bunkat.github.io/later/parsers.html#text diff --git a/src/config/production_template.yaml b/src/config/production_template.yaml index 09533c2c..c9249b7f 100644 --- a/src/config/production_template.yaml +++ b/src/config/production_template.yaml @@ -155,18 +155,32 @@ Butler: # Settings for monitoring Qlik Sense licenses qlikSenseLicense: - licenseMonitor: + serverLicenseMonitor: + enable: false + frequency: every 24 hours # https://bunkat.github.io/later/parsers.html#text + alert: # 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: 60 + destination: + influxDb: # Store license data in InfluxDB + enable: false + tag: + static: # Static attributes/tags to attach to the data sent to InflixDB + - name: foo + value: bar + licenseMonitor: # Monitor Qlik Sense accesds license usage enable: false frequency: every 6 hours # https://bunkat.github.io/later/parsers.html#text destination: influxDb: # Store license data in InfluxDB - enable: true + enable: false tag: static: # Static attributes/tags to attach to the data sent to InflixDB - name: foo value: bar - licenseRelease: - enable: false # true/false. If true, Butler will release unused licenses according to settings below + licenseRelease: # Release unused Qlik Sense access licenses + enable: false # true/false. If true, Butler will release unused licenses according to settings below dryRun: true # true/false. If true, Butler will not actually release any licenses, just log what it would have done. frequency: every 6 hours # https://bunkat.github.io/later/parsers.html#text neverRelease: # Various ways of defining which users should never have their licenses released @@ -201,7 +215,7 @@ Butler: releaseThresholdDays: 30 # Number of days a license can be unused before it is released destination: influxDb: # Store info about released licenses in InfluxDB - enable: true + enable: false tag: static: # Static attributes/tags to attach to the data sent to InflixDB - name: foo diff --git a/src/lib/assert/assert_config_file.js b/src/lib/assert/assert_config_file.js index 31e0d521..05bad8b4 100644 --- a/src/lib/assert/assert_config_file.js +++ b/src/lib/assert/assert_config_file.js @@ -1033,7 +1033,86 @@ export const configFileStructureAssert = async (config, logger) => { configFileCorrect = false; } - // Qlik Sense license monitoring + // Qlik Sense server license monitoring + if (!config.has('Butler.qlikSenseLicense.serverLicenseMonitor.enable')) { + logger.error('ASSERT CONFIG: Missing config file entry "Butler.qlikSenseLicense.serverLicenseMonitor.enable"'); + configFileCorrect = false; + } + + if (!config.has('Butler.qlikSenseLicense.serverLicenseMonitor.frequency')) { + logger.error('ASSERT CONFIG: Missing config file entry "Butler.qlikSenseLicense.serverLicenseMonitor.frequency"'); + configFileCorrect = false; + } + + if (!config.has('Butler.qlikSenseLicense.serverLicenseMonitor.alert.thresholdDays')) { + logger.error('ASSERT CONFIG: Missing config file entry "Butler.qlikSenseLicense.serverLicenseMonitor.alert.thresholdDays"'); + configFileCorrect = false; + } + + if (!config.has('Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.enable')) { + logger.error( + 'ASSERT CONFIG: Missing config file entry "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.enabled"' + ); + configFileCorrect = false; + } + + // Make sure all entries in Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static are objects with the following properties: + // { + // name: 'string', + // value: 'string' + // } + if (config.has('Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static')) { + const tagsStatic = config.get('Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static'); + + if (tagsStatic) { + if (!Array.isArray(tagsStatic)) { + logger.error( + 'ASSERT CONFIG: "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static" is not an array' + ); + configFileCorrect = false; + } else { + tagsStatic.forEach((tag, index) => { + if (typeof tag !== 'object') { + logger.error( + `ASSERT CONFIG: "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static[${index}]" is not an object` + ); + configFileCorrect = false; + } else { + if (!Object.prototype.hasOwnProperty.call(tag, 'name')) { + logger.error( + `ASSERT CONFIG: Missing "name" property in "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static[${index}]"` + ); + configFileCorrect = false; + } else if (typeof tag.name !== 'string') { + logger.error( + `ASSERT CONFIG: "name" property in "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static[${index}]" is not a string` + ); + configFileCorrect = false; + } + + if (!Object.prototype.hasOwnProperty.call(tag, 'value')) { + logger.error( + `ASSERT CONFIG: Missing "value" property in "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static[${index}]"` + ); + configFileCorrect = false; + } else if (typeof tag.value !== 'string') { + logger.error( + `ASSERT CONFIG: "value" property in "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static[${index}]" is not a string` + ); + configFileCorrect = false; + } + } + }); + } + } + } else { + logger.error( + 'ASSERT CONFIG: Missing config file entry "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.enabled"' + ); + configFileCorrect = false; + } + + // Qlik Sense access license monitoring if (!config.has('Butler.qlikSenseLicense.licenseMonitor.enable')) { logger.error('ASSERT CONFIG: Missing config file entry "Butler.qlikSenseLicense.licenseMonitor.enable"'); configFileCorrect = false; diff --git a/src/lib/post_to_influxdb.js b/src/lib/post_to_influxdb.js index b5cffb29..86a7e697 100755 --- a/src/lib/post_to_influxdb.js +++ b/src/lib/post_to_influxdb.js @@ -103,7 +103,68 @@ export async function postQlikSenseVersionToInfluxDB(qlikSenseVersion) { globals.logger.verbose('INFLUXDB QLIK SENSE VERSION: Sent Qlik Sense version to InfluxDB'); } -// Function to store Qlik Sense license status to InfluxDB +// Function to store Qlik Sense server license status to InfluxDB +// 1st parameter is an object with the following structure: +// { +// "licenseExpired": , +// "expiryDate": , +// "expiryDateStr": " +// "daysUntilExpiry": , +// } +export async function postQlikSenseServerLicenseStatusToInfluxDB(qlikSenseServerLicenseStatus) { + globals.logger.verbose('INFLUXDB QLIK SENSE SERVER LICENSE STATUS: Sending Qlik Sense server license status to InfluxDB'); + + const instanceTag = globals.config.has('Butler.influxDb.instanceTag') ? globals.config.get('Butler.influxDb.instanceTag') : ''; + + // Get tags from config file + // Stored in array Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag + const configTags = globals.config.get('Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static'); + + const tags = { + butler_instance: instanceTag, + }; + + // Add tags from config file + if (configTags) { + // eslint-disable-next-line no-restricted-syntax + for (const item of configTags) { + tags[item.name] = item.value; + } + } + + // Do a deep clone of the tags object + const tagsCloned = _.cloneDeep(tags); + + // Build InfluxDB datapoint + let datapoint = [ + { + measurement: 'qlik_sense_server_license', + tags: tagsCloned, + fields: { + license_expired: qlikSenseServerLicenseStatus.licenseExpired, + expiry_date: qlikSenseServerLicenseStatus.expiryDateStr, + days_until_expiry: qlikSenseServerLicenseStatus.daysUntilExpiry, + }, + }, + ]; + + // Write to InfluxDB + const deepClonedDatapoint = _.cloneDeep(datapoint); + await globals.influx.writePoints(deepClonedDatapoint); + + globals.logger.silly( + `INFLUXDB QLIK SENSE SERVER LICENSE STATUS: Influxdb datapoint for Qlik Sense server license status: ${JSON.stringify( + datapoint, + null, + 2 + )}` + ); + + datapoint = null; + globals.logger.verbose('INFLUXDB QLIK SENSE SERVER LICENSE STATUS: Sent Qlik Sense server license status to InfluxDB'); +} + +// Function to store Qlik Sense access license status to InfluxDB // License JSON has the following structure: // { // "totalTokens": 0, diff --git a/src/lib/qliksense_license.js b/src/lib/qliksense_license.js index e9106f84..90e1ce79 100644 --- a/src/lib/qliksense_license.js +++ b/src/lib/qliksense_license.js @@ -2,10 +2,143 @@ import later from '@breejs/later'; import QrsInteract from 'qrs-interact'; import globals from '../globals.js'; -import { postQlikSenseLicenseStatusToInfluxDB, postQlikSenseLicenseReleasedToInfluxDB } from './post_to_influxdb.js'; +import { + postQlikSenseLicenseStatusToInfluxDB, + postQlikSenseLicenseReleasedToInfluxDB, + postQlikSenseServerLicenseStatusToInfluxDB, +} from './post_to_influxdb.js'; + +// Function to check Qlik Sense server license status +async function checkQlikSenseServerLicenseStatus(config, logger) { + try { + // Set up Sense repository service configuration + const configQRS = { + hostname: globals.config.get('Butler.configQRS.host'), + portNumber: 4242, + certificates: { + certFile: globals.configQRS.certPaths.certPath, + keyFile: globals.configQRS.certPaths.keyPath, + }, + }; + + configQRS.headers = { + 'X-Qlik-User': 'UserDirectory=Internal; UserId=sa_repository', + }; + const qrsInstance = new QrsInteract(configQRS); + + // Get Qlik Sense server license info + const result = await qrsInstance.Get(`license`); + + // Is status code 200 or body is empty? + if (result.statusCode !== 200 || !result.body) { + logger.error(`QLIKSENSE SERVER LICENSE MONITOR: HTTP status code ${result.statusCode}`); + return; + } + + // Debug log + logger.debug(`QLIKSENSE SERVER LICENSE MONITOR: ${JSON.stringify(result.body)}`); + + // Returned icense JSON has the following structure: + // { + // "id": "", + // "createdDate": "2000-01-31T03:49:21.745Z", + // "modifiedDate": "2024-01-31T19:44:28.951Z", + // "modifiedByUserName": "", + // "lef": "", + // "serial": "0001 0002 0003 0004", + // "check": "", + // "key": "", + // "keyDetails": "", + // "organization": "", + // "product": , + // "numberOfCores": , + // "isExpired": , + // "expiredReason": "", + // "isBlacklisted": , + // "isInvalid": , + // "isSubscription": , + // "isCloudServices": , + // "isElastic": , + // "updated": "2024-02-25T09:17:16.366Z", + // "privileges": null, + // "schemaPath": "License" + // } + + // Parse license expiry from server license status + let expiryDate = null; + let expiryDateStr = null; + let daysUntilExpiry = null; + let licenseExpired = null; + + // Is license expired flag set? + if (result.body.isExpired === true) { + licenseExpired = true; + } else { + licenseExpired = false; + } + + // Find license expiration date from keyDatails property, which is string made up of several blocks of information. + // The expiration date is found in a line formatted as: "Valid To: " + const keyDetailsLines = result.body.keyDetails.split('\n'); + keyDetailsLines.forEach((line) => { + if (line.includes('Valid To:')) { + const parts = line.split(':'); -// Function to check Qlik Sense license status -async function checkQlikSenseLicenseStatus(config, logger) { + // Format date as string + expiryDate = new Date(parts[1].trim()); + expiryDateStr = parts[1].trim(); + } + }); + + // Calculate days until license expiry + if (expiryDateStr !== null) { + const now = new Date(); + const diffTime = Math.abs(expiryDate - now); + daysUntilExpiry = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + } + + // Log info about license expiration status, date and remaining days to console log. + // Use warn logging if days until expiry is less than value specified in config file, + // otherwise use info logging + if (daysUntilExpiry !== null && expiryDateStr !== null && licenseExpired !== null) { + globals.logger.info(`QLIK SENSE SERVER LICENSE: License expired: ${licenseExpired}`); + + if (daysUntilExpiry <= globals.config.get('Butler.qlikSenseLicense.serverLicenseMonitor.alert.thresholdDays')) { + globals.logger.warn(`QLIK SENSE SERVER LICENSE: Qlik Sense server license is about to expire in ${daysUntilExpiry} days!`); + globals.logger.warn(`QLIK SENSE SERVER LICENSE: Expiry date: ${expiryDate}`); + } else { + globals.logger.info(`QLIK SENSE SERVER LICENSE: Qlik Sense server license expiry in ${daysUntilExpiry} days`); + globals.logger.info(`QLIK SENSE SERVER LICENSE: Expiry date: ${expiryDate}`); + } + } + + // To which destination should we send the license information? + // Check InfluDB first + // If InfluxDB is enabled, post the license status to InfluxDB + if ( + config.has('Butler.influxDb.enable') && + config.get('Butler.influxDb.enable') === true && + config.has('Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.enable') && + config.get('Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.enable') === true + ) { + await postQlikSenseServerLicenseStatusToInfluxDB({ + licenseExpired, + expiryDate, + expiryDateStr, + daysUntilExpiry, + }); + } + } catch (err) { + logger.error(`QLIKSENSE SERVER LICENSE MONITOR: ${err}`); + if (err.stack) { + logger.error(`QLIKSENSE SERVER LICENSE MONITOR: ${err.stack}`); + } + } +} + +// Function to check Qlik Sense access license status +async function checkQlikSenseAccessLicenseStatus(config, logger) { try { // Set up Sense repository service configuration const configQRS = { @@ -22,7 +155,7 @@ async function checkQlikSenseLicenseStatus(config, logger) { }; const qrsInstance = new QrsInteract(configQRS); - // Get Qlik Sense license info + // Get Qlik Sense access license info const result1 = await qrsInstance.Get(`license/accesstypeoverview`); // Is status code 200 or body is empty? @@ -53,7 +186,7 @@ async function checkQlikSenseLicenseStatus(config, logger) { } } -// Function to release professional licenses +// Function to release professional access licenses async function licenseReleaseProfessional(config, logger, qrsInstance) { // Build date filter to be used when fetching licenses with old lastUsed date // Get the current date and time @@ -134,13 +267,15 @@ async function licenseReleaseProfessional(config, logger, qrsInstance) { let doNotRelease = false; let doNotReleaseReason = ''; - // Check do-not-release user names - // eslint-disable-next-line no-restricted-syntax - for (const user of neverReleaseUsers) { - if (license.user.userDirectory === user.userDir && license.user.userId === user.userId) { - doNotRelease = true; - doNotReleaseReason = 'User is in the neverRelease.user list'; - break; + // Check do-not-release user names if there are any + if (neverReleaseUsers?.length > 0) { + // eslint-disable-next-line no-restricted-syntax + for (const user of neverReleaseUsers) { + if (license.user.userDirectory === user.userDir && license.user.userId === user.userId) { + doNotRelease = true; + doNotReleaseReason = 'User is in the neverRelease.user list'; + break; + } } } @@ -148,22 +283,24 @@ async function licenseReleaseProfessional(config, logger, qrsInstance) { // If... // - the user is not already marked as doNotRelease=true and // - the currentUser does not haven any neverReleaseTags set - if (!doNotRelease) { + if (!doNotRelease && currentUser.tags?.length > 0) { // Check if the user has any of the neverReleaseTags set // currentUser.tags is an array of tag objects. Each object has properties id and name // eslint-disable-next-line no-restricted-syntax for (const tag of currentUser.tags) { - // eslint-disable-next-line no-restricted-syntax - for (const neverReleaseTag of neverReleaseTags) { - if (tag.name === neverReleaseTag) { - doNotRelease = true; - doNotReleaseReason = `User tagged with '${neverReleaseTag}', which is in the neverRelease.tag list`; + if (neverReleaseTags?.length > 0) { + // eslint-disable-next-line no-restricted-syntax + for (const neverReleaseTag of neverReleaseTags) { + if (tag.name === neverReleaseTag) { + doNotRelease = true; + doNotReleaseReason = `User tagged with '${neverReleaseTag}', which is in the neverRelease.tag list`; + break; + } + } + if (doNotRelease) { break; } } - if (doNotRelease) { - break; - } } } @@ -171,7 +308,7 @@ async function licenseReleaseProfessional(config, logger, qrsInstance) { // If... // - the user is not already marked as doNotRelease=true and // - the currentUser does not have any neverReleaseCustomProperties set - if (!doNotRelease) { + if (!doNotRelease && currentUser.customProperties?.length > 0) { // currentUser.customProperties is an array of custom property objects. // Each object looks like this: // { @@ -185,20 +322,22 @@ async function licenseReleaseProfessional(config, logger, qrsInstance) { // } // eslint-disable-next-line no-restricted-syntax for (const customProperty of currentUser.customProperties) { - // eslint-disable-next-line no-restricted-syntax - for (const neverReleaseCustomProperty of neverReleaseCustomProperties) { - if ( - customProperty.definition.name === neverReleaseCustomProperty.name && - customProperty.value === neverReleaseCustomProperty.value - ) { - doNotRelease = true; - doNotReleaseReason = `User has custom property '${neverReleaseCustomProperty.name}' set to '${neverReleaseCustomProperty.value}', which is in the neverRelease.customProperty list`; + if (neverReleaseCustomProperties?.length > 0) { + // eslint-disable-next-line no-restricted-syntax + for (const neverReleaseCustomProperty of neverReleaseCustomProperties) { + if ( + customProperty.definition.name === neverReleaseCustomProperty.name && + customProperty.value === neverReleaseCustomProperty.value + ) { + doNotRelease = true; + doNotReleaseReason = `User has custom property '${neverReleaseCustomProperty.name}' set to '${neverReleaseCustomProperty.value}', which is in the neverRelease.customProperty list`; + break; + } + } + if (doNotRelease) { break; } } - if (doNotRelease) { - break; - } } } @@ -206,7 +345,7 @@ async function licenseReleaseProfessional(config, logger, qrsInstance) { // If... // - the user is not already marked as doNotRelease=true and // - the currentUser does not have any neverReleaseUserDirectories set - if (!doNotRelease) { + if (!doNotRelease && neverReleaseUserDirectories?.length > 0) { // eslint-disable-next-line no-restricted-syntax for (const neverReleaseUserDir of neverReleaseUserDirectories) { if (license.user.userDirectory === neverReleaseUserDir) { @@ -333,7 +472,7 @@ async function licenseReleaseProfessional(config, logger, qrsInstance) { return true; } -// Function to release analyzer licenses +// Function to release analyzer access licenses async function licenseReleaseAnalyzer(config, logger, qrsInstance) { // Build date filter to be used when fetching licenses with old lastUsed date // Get the current date and time @@ -611,7 +750,7 @@ async function licenseReleaseAnalyzer(config, logger, qrsInstance) { return true; } -// Function to release Qlik Sense licenses +// Function to release Qlik Sense access licenses async function checkQlikSenseLicenseRelease(config, logger) { try { // Set up Sense repository service configuration @@ -661,8 +800,8 @@ async function checkQlikSenseLicenseRelease(config, logger) { } } -// Function to set up the timer used to check Qlik Sense license status -export async function setupQlikSenseLicenseMonitor(config, logger) { +// Function to set up the timer used to check Qlik Sense access license status +export async function setupQlikSenseAccessLicenseMonitor(config, logger) { try { if ( !config.has('Butler.qlikSenseLicense.licenseMonitor.enable') || @@ -670,12 +809,12 @@ export async function setupQlikSenseLicenseMonitor(config, logger) { ) { const sched = later.parse.text(config.get('Butler.qlikSenseLicense.licenseMonitor.frequency')); later.setInterval(() => { - checkQlikSenseLicenseStatus(config, logger, false); + checkQlikSenseAccessLicenseStatus(config, logger, false); }, sched); // Do an initial license check logger.verbose('Doing initial Qlik Sense license check'); - checkQlikSenseLicenseStatus(config, logger, true); + checkQlikSenseAccessLicenseStatus(config, logger, true); } } catch (err) { logger.error(`QLIKSENSE LICENSE MONITOR INIT: ${err}`); @@ -685,7 +824,7 @@ export async function setupQlikSenseLicenseMonitor(config, logger) { } } -// Function to set up the timer used to release Qlik Sense licenses +// Function to set up the timer used to release Qlik Sense access licenses export async function setupQlikSenseLicenseRelease(config, logger) { try { if ( @@ -708,3 +847,27 @@ export async function setupQlikSenseLicenseRelease(config, logger) { } } } + +// Function to set up the timer used to check Qlik Sense server license status +export async function setupQlikSenseServerLicenseMonitor(config, logger) { + try { + if ( + !config.has('Butler.qlikSenseLicense.serverLicenseMonitor.enable') || + config.get('Butler.qlikSenseLicense.serverLicenseMonitor.enable') === true + ) { + const sched = later.parse.text(config.get('Butler.qlikSenseLicense.serverLicenseMonitor.frequency')); + later.setInterval(() => { + checkQlikSenseServerLicenseStatus(config, logger, false); + }, sched); + + // Do an initial license check + logger.verbose('Doing initial Qlik Sense server license check'); + checkQlikSenseServerLicenseStatus(config, logger, true); + } + } catch (err) { + logger.error(`QLIKSENSE SERVER LICENSE MONITOR INIT: ${err}`); + if (err.stack) { + logger.error(`QLIKSENSE SERVER LICENSE MONITOR INIT: ${err.stack}`); + } + } +} diff --git a/src/lib/qliksense_version.js b/src/lib/qliksense_version.js index 8afba5d6..1696ade9 100644 --- a/src/lib/qliksense_version.js +++ b/src/lib/qliksense_version.js @@ -35,6 +35,12 @@ async function checkQlikSenseVersion(config, logger) { // Debug log logger.debug(`QLIKSENSE VERSION MONITOR: ${JSON.stringify(result.data)}`); + // Log version info to console log + logger.info(`QLIKSENSE VERSION MONITOR: Qlik Sense product name: ${result.data.productName}`); + logger.info(`QLIKSENSE VERSION MONITOR: Qlik Sense deployment type: ${result.data.deploymentType}`); + logger.info(`QLIKSENSE VERSION MONITOR: Qlik Sense version: ${result.data.version}`); + logger.info(`QLIKSENSE VERSION MONITOR: Qlik Sense release: ${result.data.releaseLabel}`); + // To which destination should we send the version information? // Check InfluDB first // If InfluxDB is enabled, post the version info to InfluxDB