Like the diagnostic dashboard, I made a project with a dashboard to show the threads on the gateway. I'm using system.util.threadDump() then parsing that json to get how many of each type of thread and the status.
However, I feel like there may be a better method that's not so resource intensive.
Thanks for the info. As having never worked with APIs before, sounds like it's time to learn.
In case anyone was curious, this is what I was running on the gateway every 10 seconds, I had it running every second, but that was... problematic. (putting it lightly)
import json
threadDumpStr = system.util.threadDump()
threadDump = json.loads(threadDumpStr)
webserverTag = '[default]Dashboard/Threads/WebserverThreadCount'
timed_waitingTag = '[default]Dashboard/Threads/TIMED_WAITING'
# Count webserver threads
webserverCount = sum(1 for thread in threadDump['threads'] if thread['name'].startswith('webserver'))
# Count threads with TIMED_WAITING status
timedWaitingCount = sum(1 for thread in threadDump['threads'] if thread['state'] == 'TIMED_WAITING' and thread['name'].startswith('webserver'))
# Write the dataset to a tag
system.tag.write(webserverTag, webserverCount)
system.tag.write(timed_waitingTag, timedWaitingCount)
If you're not paying any attention to the stack trace, you can avoid fetching it (ThreadMXBean (Java SE 17 & JDK 17)) which will make the calls significantly cheaper.
I tried that and it doesn't seem like I'm getting the full thread dump from the gateway.
from java.lang.management import ManagementFactory
from java.lang import Thread
# Get the ThreadMXBean instance
thread_mx_bean = ManagementFactory.getThreadMXBean()
# Get a dump of all threads
thread_info_array = thread_mx_bean.dumpAllThreads(True, True)
# Initialize counters
webserver_count = 0
timed_waiting_count = 0
# Process the thread_info_array
for thread_info in thread_info_array:
# Check if the thread name starts with "Webserver"
if thread_info.getThreadName().startswith("Webserver"):
webserver_count += 1
# Check if the thread state is TIMED_WAITING
if thread_info.getThreadState() == Thread.State.TIMED_WAITING:
timed_waiting_count += 1
# Write the counts to the specified tags
system.tag.write('[default]Dashboard/Threads/WebserverThreadCount', webserver_count)
system.tag.write('[default]Dashboard/Threads/TIMED_WAITING', timed_waiting_count)
If I change the .startswith() to something like "Swing" or "Fork" I get some results. With "Webserver" I would expect a "webserver" thread count of around 80 because that's what I can see on the VM when I do a thread dump there.
That implies you're calling this from a client or designer scope, not the actual gateway. You need to call the script on the gateway to get gateway scoped results.
I've got it running as a gateway event under scripting, shouldn't that be the correct scope? Or is there something I'm missing with how I'm calling the function?
I understand that if I run it in the script console that it would be the designer scope, but the saved script in the gateway events should run from the gateway?
If it's running as a gateway event, then yes, it should be executing on the gateway, but you also definitely shouldn't see any Swing related threads on the gateway.
Do you have Perspective? Can you run it from a button in Perspective to be sure?
Maybe also print out thread_info_array?
Correct, I only got Swing threads when I ran it from the script console in designer.
Okay, I got it working from the button. I found a few issues. I was calling dumpAllThreads when I should have been calling getThreadInfo
also .startswith() is case sensitive.
from java.lang.management import ManagementFactory
from java.lang import Thread
# Get the ThreadMXBean instance
thread_mx_bean = ManagementFactory.getThreadMXBean()
# Get all live thread IDs
thread_ids = thread_mx_bean.getAllThreadIds()
# Get thread information for the specified threads
thread_info_array = thread_mx_bean.getThreadInfo(thread_ids)
# thread_info_array to text block
self.parent.parent.getChild("TextArea").props.text = thread_info_array
# Initialize counters
webserver_count = 0
timed_waiting_count = 0
# Process the thread_info_array
for thread_info in thread_info_array:
if thread_info is not None:
# Check if the thread name starts with "Webserver"
if thread_info.getThreadName().startswith("webserver"):
webserver_count += 1
# Check if the thread state is TIMED_WAITING
if thread_info.getThreadState() == Thread.State.TIMED_WAITING:
timed_waiting_count += 1
# Write the counts to the specified tags
system.tag.write('[default]Dashboard/Threads/WebserverThreadCount', webserver_count)
system.tag.write('[default]Dashboard/Threads/TIMED_WAITING', timed_waiting_count)
So it's working fine from the button but not from the gateway event script with a 10,000 ms timer.
That is legacy behavior in gateway events. One of the reasons I strongly recommend never writing an actual script in the event. Just put a one-liner there that calls a library script function. Library scripts don't have the funky problems with nested scopes.