Select to view content in your preferred language

License Manager logging

161
0
07-16-2025 11:12 PM
rdbutger
Emerging Contributor

I can't figure out where best to post this -- so putting here, and hopefully maybe useful to others. 

Following up on this  https://community.esri.com/t5/esri-technical-support-blog/where-did-my-license-go/ba-p/897791 -- we needed to get better information on our users using concurrent licenses with ArcMap and ArcGIS Pro. 

1 - Start logging on your license server. Write out a text file every 3,4,6 hours -- whatever makes sense to you. a sample Scheduled Task to run a batch file like this -- 

@echooff
setlocal

:: Get current date and time
for /f "tokens=1-4 delims=/ " %%a in ("%date%") do set date=%%d-%%b-%%c
for /f "tokens=1-2 delims=: " %%a in ("%time%") do set time=%%a%%b

:: Format the time to remove spaces and colons
set time=%time: =%
set time=%time::=%

:: Create Logs directory if it doesn't exist
if not exist Logs mkdir Logs

:: Run lmutil command and output to file in Logs directory with date and time in the name
cd \Esri_Logging
lmutil.exe lmstat -a -c @yourserver> c:\Esri_Logging\Logs\ESRILicenseUse-%date%-%time%.txt

endlocal

That will create log files. 

2 - Let that run a while, and then you'll get some log files. 

3 - This Python can be used to give you spreadsheets from those logs rich with information -- 

import os
import re
import csv
from collections import defaultdict

print("Starting script...")

# Define the regex patterns
date_time_pattern = re.compile(r'Flexible License Manager status on (\w+ \d+/\d+/\d+ \d+:\d+)')
feature_pattern = re.compile(r'Users of (\S+):\s+\(Total of \d+ licenses issued; Total of (\d+) licenses in use\)')
activated_license_start_pattern = re.compile(r'ACTIVATED LICENSE\(S\)', re.IGNORECASE)
license_line_pattern = re.compile(r'^\s*(\w+)\s+([A-Z0-9\-]+)', re.MULTILINE)
activation_line_pattern = re.compile(r'^\s*ACTIVATED LICENSE\(S\)\s+([A-Z0-9\-]+)\s+ACTIVATION', re.IGNORECASE)

# Features to track
feature_dict = {
'desktopBasicN': 'ArcGIS Desktop Basic (Network license)',
'ACT': 'ArcGIS Pro Advanced (legacy code)',
'3DAnalystP': '3D Analyst for ArcGIS Pro',
'ARC/INFO': 'ArcGIS Desktop Advanced (ArcMap)',
'ArcStorm': 'ArcStorm extension',
'ArcStormEnable': 'ArcStorm Enablement',
'dataInteropP': 'Data Interoperability for ArcGIS Pro',
'DataReViewer': 'Data Reviewer (ArcMap)',
'dataReviewerP': 'Data Reviewer for ArcGIS Pro',
'Defense': 'Defense Mapping',
'desktopAdvP': 'ArcGIS Pro Advanced',
'Foundation': 'Production Mapping Foundation',
'geostatAnalystP': 'Geostatistical Analyst for ArcGIS Pro',
'GeoStats': 'Geostatistical Analyst (ArcMap)',
'Grid': 'Spatial Analyst (ArcMap)',
'Interop': 'Data Interoperability (ArcMap)',
'JTX': 'Workflow Manager (ArcMap)',
'Maplex': 'Maplex Label Engine',
'MrSID': 'MrSID Image Support',
'Network': 'Network Analyst (ArcMap)',
'networkAnalystP': 'Network Analyst for ArcGIS Pro',
'Plotting': 'Plotting extension',
'Publisher': 'Publisher extension (ArcMap)',
'publisherP': 'Publisher for ArcGIS Pro',
'Schematics': 'Schematics extension',
'spatialAnalystP': 'Spatial Analyst for ArcGIS Pro',
'TIFFLZW': 'TIFF LZW Compression Support',
'TIN': 'TIN Analyst',
'workflowMgrP': 'Workflow Manager for ArcGIS Pro'
}

tracked_features = list(feature_dict.keys())
features_desc = list(feature_dict.values())

# Directory containing the log files
log_directory = r'\\YourLinseServer\Esri_Logging\Logs'

# Output CSV files
summary_csv = 'license_usage_summary.csv'
arcgispro_csv = 'users_arcgis_pro.csv'
arcmap_csv = 'users_arcmap.csv'
user_counts_csv = 'user_license_counts.csv'

# Write to all four output files
with open(summary_csv, mode='w', newline='') as summary_file, \
open(arcgispro_csv, mode='w', newline='') as pro_file, \
open(arcmap_csv, mode='w', newline='') as map_file, \
open(user_counts_csv, mode='w', newline='') as count_file:

summary_writer = csv.writer(summary_file)
pro_writer = csv.writer(pro_file)
map_writer = csv.writer(map_file)
count_writer = csv.writer(count_file)

# Write headers
summary_writer.writerow(['', ''] + features_desc)
summary_writer.writerow(['Date', 'Time'] + tracked_features)
pro_writer.writerow(['Date', 'Time', 'Username', 'Machine'])
map_writer.writerow(['Date', 'Time', 'Username', 'Machine'])
count_writer.writerow(['Date', 'Time', 'Username', 'Machine', 'ArcMap_Count', 'ArcGIS_Pro_Count'])

# Iterate through log files
for filename in os.listdir(log_directory):
if filename.endswith('.txt'):
filepath = os.path.join(log_directory, filename)
print(f"Processing {filename}")

with open(filepath, 'r', encoding='utf-8', errors='ignore') as file:
content = file.read()

# Extract date and time
date_time_match = date_time_pattern.search(content)
if date_time_match:
date_time = date_time_match.group(1)
date, time = date_time.rsplit(' ', 1)
else:
date, time = 'Unknown', 'Unknown'

# Feature usage summary
usage = {feature: '0' for feature in tracked_features}
for match in feature_pattern.finditer(content):
feature, in_use = match.groups()
if feature in usage:
usage[feature] = in_use

summary_writer.writerow([date, time] + [usage[feat] for feat in tracked_features])

# Track user license counts
user_license_counts = defaultdict(lambda: {'ArcMap': 0, 'ArcGIS_Pro': 0})
user_machines = {}

# Process ArcMap and ArcGIS Pro sections
for feature_code, writer, category in [
('desktopAdvP', pro_writer, 'ArcGIS_Pro'),
('ARC/INFO', map_writer, 'ArcMap')
]:
section_pattern = re.compile(
rf'Users of {re.escape(feature_code)}:.*?ACTIVATED LICENSE\(S\)(.*?)(?=Users of|\Z)',
re.DOTALL | re.IGNORECASE
)
section_match = section_pattern.search(content)
if section_match:
license_block = section_match.group(1)
for line in license_block.splitlines():
activation_match = activation_line_pattern.match(line)
if activation_match:
machine = activation_match.group(1)
username = "ACTIVATED LICENSE(S)"
writer.writerow([date, time, username, machine])
user_license_counts[username][category] += 1
user_machines[username] = machine
continue

user_match = license_line_pattern.match(line)
if user_match:
username, machine = user_match.groups()
writer.writerow([date, time, username, machine])
user_license_counts[username][category] += 1
user_machines[username] = machine

# Write user license counts
for username, counts in user_license_counts.items():
machine = user_machines.get(username, '')
count_writer.writerow([date, time, username, machine, counts['ArcMap'], counts['ArcGIS_Pro']])

print(f"Done! Output saved to:\n- {summary_csv}\n- {arcgispro_csv}\n- {arcmap_csv}\n- {user_counts_csv}")

4 - Take those CSVs into Excel, merge, sum, chart and get lots of details on your users and their use of concurrent licenses.

Yes, this post could have probably been more useful to the community like 6 months ago!




0 Kudos
0 Replies