Forum Discussion

jbellassai's avatar
jbellassai
New Contributor
13 years ago

Custom statistics from distributed agents

Hi, I worked out a quick and dirty custom component which takes statistics gathered from the results map of a Script Runner and makes those statistics available in the Results Workbench for graphing. This seems to work well in Local Mode, however I get no results when running in Distributed Mode. I imagine it has something to do with the fact that the statistical results collected from the agents are not being collected by the local controller at the end of the run.

I noticed that there are TODO comments in the Component Handlers (http://www.loadui.org/Developers-Corner/custom-component-reference-new.html#4-3-component-handlers) section for collectStatisticsData and handleStatisticsData which I suspect I may need to implement for this to work as expected.

Any chance one of you wonderful folks could give me a hint on how to properly implement those handlers? Also, thanks for an awesome product!

3 Replies

  • SmartBear_Suppo's avatar
    SmartBear_Suppo
    SmartBear Alumni (Retired)
    Hi, I'm glad to hear that you're making a custom component

    The undocumented API methods are just for dealing with the summary report, so it's not of interest in this case.

    Are you 100% positive that the Script Runner is actually running correctly on the agents? You could check that by connecting it to a TableLog.

    To help you further, I would need to take a look at your component code. Could you share it here?

    Henrik
    SmartBear Software
  • jbellassai's avatar
    jbellassai
    New Contributor
    Thanks for the reply. As it turns out, I did end up getting this to work correctly, though now I am unsure what I did

    As requested, here is the code for the component:

    /**
    *
    * @name Custom Statistics
    * @category analysis
    * @id com.example.CustomStatistics
    */

    //Properties
    createProperty('stats', String, '')

    customStats = [:]

    createStats = {

    if(customStats) {
    for(stat in customStats) {
    log.info("Removing stat " + stat.key)
    removeStatisticVariable(stat.key)
    }
    customStats = [:]
    }

    if(!stats.value) {
    return [:]
    }

    log.debug("Parsing stats String $stats.value")
    splitStats = stats.value.split(' *, *')
    log.debug("Split stats=$splitStats")

    ret = [:]
    for(stat in splitStats) {
    splitStat = stat.split(':')
    def statName = splitStat[0]
    def statType = 'SAMPLE'
    if(splitStat.length > 1) {
    statType = splitStat[1]
    }

    log.info("Adding new stats variable '$statName' of type '$statType'")
    newStat = addStatisticVariable(statName, statType)
    ret[statName] = newStat
    }

    customStats = ret
    }

    analyze = { message ->
    now = System.currentTimeMillis()
    try {
    if(message instanceof Map) {
    if(message['stats'] instanceof Map) {
    theseStats = message['stats']
    for(customStatEntry in customStats) {

    statName = customStatEntry.key
    stat = customStatEntry.value

    statValue = theseStats[statName]?.value

    if(statValue) {
    try {
    //log.info("Updating stat $statName: $statValue")
    if(statValue instanceof Number) {
    stat.update(now, statValue)
    }
    else if(statValue instanceof String) {
    stat.update(now, Long.parseLong(statValue))
    }
    else {
    log.error("value for stat '$statName' is not a Number or String! : $statValue")
    }
    } catch(NumberFormatException ignored) {
    log.error("Unable to parse value for stat '$statName'", e)
    }
    }
    else {
    log.error("No value for stat '$statName'")
    }
    }
    }
    }
    }
    catch(Exception e) {
    log.error('analyze', e)
    }
    }

    collectStatisticsData = {
    log.debug("In collectStatisticsData")
    }

    handleStatisticsData = { statisticsData ->
    log.debug("handleStatisticsData: $statisticsData")
    }

    onAction("RESET") {
    createStats()
    }

    layout {
    box( layout: 'wrap 2, ins 0' ) {
    property( property: stats, label: 'Expected Statistics', constraints: 'growx, spanx 2' )
    }
    }

    createStats()
    onReplace(stats, createStats)


    Basically, you input a comma-delimited set of expected stat names (and optionally type) and the component registers a new statistic variable with that name and type. Then, it expects that the message passed in contains a key called 'stats' whose value is another map where the keys are stat names and the values are the numeric values to pass to the stat.update() method. I hope that makes sense.

    Like I said, this was kind of quick and dirty component and there are definitely some improvements that could be made, but it seems to get the job done!

    By the way, since I have you on the line, I was wondering if there was any way to clear the System Log and/or set the number of log entries that are kept? Since I've been dabbling in writing custom components, once in a while I'll see a stack trace get generated, but I can't get to it before it is pushed off the log by newer entries.

    Thanks again!
  • SmartBear_Suppo's avatar
    SmartBear_Suppo
    SmartBear Alumni (Retired)
    Hi,

    I think the reason it isn't working is because you are not adding any StatisticsWriter to the StatisticVariables you're creating. I don't think it will work without at least one. Each StatisticsWriter provides one or more counters for derived values of the raw data you're feeding into the update method. The available ones are:


    COUNTER, SAMPLE, THROUGHPUT, VARIABLE


    Which of these you should use depend on the nature of the data you're feeding into the component, but you may want to give SAMPLE a try initially:


    addStatisticVariable(statName, statType, "SAMPLE")


    Regards,
    Dain
    SmartBear Software