Monitoring License Consumption
Our app lacks built-in functionality to generate reports on the availability of licenses over a specific period. We decided not to implement this, mainly to prevent additional overhead and performance impacts. Please find below two approaches for getting the maximum number of concurrent users per day. This number will help you decide which user tier you can downgrade to after enabling License Optimizer.
Monitoring via REST
This requires License Optimizer to be enabled. Atlassian REST endpoints provide information about license consumption, enabling you to write a script that fetches the data periodically.
Jira
Either fetch data with a GET request for all applications (1) or a single application (2-4)
- https://your-jira.com/rest/api/2/applicationrole
- https://your-jira.com/rest/api/2/applicationrole/jira-core
- https://your-jira.com/rest/api/2/applicationrole/jira-software
- https://your-jira.com/rest/api/2/applicationrole/jira-servicedesk
The reply looks like this and contains a field remainingSeats that reflects the number of available licenses at the time.
- {
- "key": "jira-core",
- "groups": [
- "jira-administrators",
- "jira-core-users"
- ],
- "name": "Jira Core",
- "defaultGroups": [
- "jira-core-users"
- ],
- "selectedByDefault": true,
- "defined": true,
- "numberOfSeats": 500,
- "remainingSeats": 210,
- "userCount": 0,
- "userCountDescription": "users",
- "hasUnlimitedSeats": true,
- "platform": true
- }
Confluence
Fetch data with the following GET request
- https://your-confluence.com/rest/license/1.0/license/remainingSeats
The reply looks like this and provides the available licenses at the time:
- {
- "count": 48
- }
Monitoring via Database
If you install the User Deactivator app in production today, even without enabling License Optimizer, it will still start tracking your users' last activity already.
This is the better solution if you don’t want to enable License Optimizer already but still want to get as close as possible to the maximum number of concurrent users per day.
If it ran for at least a day, you can query the database to retrieve a list containing usernames, email addresses, and the most recent activity.
The following are for Confluence and different DB types. For Jira, they are almost identical, with the exception that you need to refer to cwd_user_attributes
instead of cwd_user_attribute
as the table for the user attributes (line with JOIN cwd_user_attribute AS ua
becomes JOIN cwd_user_attributes AS ua
)
PostgreSQL
- SELECT
- u.lower_user_name,
- u.lower_email_address,
- ua.attribute_value::bigint AS lastactivitymillis,
- to_timestamp(ua.attribute_value::bigint / 1000.0) AS last_activity_at
- FROM cwd_user AS u
- JOIN cwd_user_attribute AS ua
- ON ua.user_id = u.id
- AND ua.attribute_name = 'lastActivityMillis'
- ORDER BY ua.attribute_value::bigint DESC NULLS LAST;
MySQL / MariaDB
- SELECT
- u.lower_user_name,
- u.lower_email_address,
- ua.attribute_value AS lastactivitymillis,
- FROM_UNIXTIME(ua.attribute_value / 1000) AS last_activity_at
- FROM cwd_user AS u
- JOIN cwd_user_attribute AS ua
- ON ua.user_id = u.id
- AND ua.attribute_name = 'lastActivityMillis'
- ORDER BY CAST(ua.attribute_value AS UNSIGNED) DESC;
SQL Server (T‑SQL)
- SELECT
- u.lower_user_name,
- u.lower_email_address,
- ua.attribute_value AS lastactivitymillis,
- DATEADD(SECOND, CAST(ua.attribute_value AS bigint) / 1000, '1970-01-01') AS last_activity_at
- FROM cwd_user AS u
- JOIN cwd_user_attribute AS ua
- ON ua.user_id = u.id
- AND ua.attribute_name = 'lastActivityMillis'
- ORDER BY CAST(ua.attribute_value AS bigint) DESC;
Oracle
- SELECT
- u.lower_user_name,
- u.lower_email_address,
- ua.attribute_value AS lastactivitymillis,
- (TIMESTAMP '1970-01-01 00:00:00 UTC'
- + NUMTODSINTERVAL(TO_NUMBER(ua.attribute_value) / 1000, 'SECOND')) AS last_activity_at
- FROM cwd_user u
- JOIN cwd_user_attribute ua
- ON ua.user_id = u.id
- AND ua.attribute_name = 'lastActivityMillis'
- ORDER BY TO_NUMBER(ua.attribute_value) DESC NULLS LAST;