Stopwatch

Monitor your production Scala (and other JVM) applications.

This small Scala library can be used to determine application performance bottlenecks, user/application interactions, and application scalability. Each stopwatch gathers summary statistics such as hits, execution times (total, average, minimum, maximum, standard deviation), distribution, simultaneous requests or concurrent threads.

Usage

Measuring your code

import stopwatch.Stopwatch

// A stopwatch surrounds your code
Stopwatch("someName") {
  // code being timed
  doSomething()
  somethingElse()
}

Non-inlined code

Sometimes you want to measure the performance of an operation which is not emcompassed by a single code section. You can then use the start and stop methods to delimit the scope of the operation.

// get a stopwatch instance
val stopwatch = Stopwatch.start("test")

// and later, elsewhere in your code
// use the same instance to signal completion
stopwatch.stop()

// if there was an error, use error() instead
stopwatch.error()

These start, stop and error methods can also be used directly by JVM languages other than Scala (e.g. Java, Closure, Groovy) to provide more idiomatic wrappers for inlined code measurement.

Groups

StopwatchGroup ’s are useful to organize stopwatches, avoid name clashes, and specify behavior for multiple stopwatches at once.

The singleton Stopwatch object that has been used so far in the examples is actually the default StopwatchGroup. You can define other stopwatch groups, each having their own distribution range and enabled/disabled state.

By default, groups are disabled and have no defined range.

import stopwatch.Stopwatch
import stopwatch.StopwatchGroup
import stopwatch.TimeUnit._

val incomingRequests = new StopwatchGroup("Incoming Requests")

// configure
incomingRequests.enabled = true
incomingRequests.range = StopwatchRange(0 seconds, 30 seconds, 1000 millis)

// usage for code blocks
incomingRequests(clientId) {
  doStuffForClient()
}

Enabling and disabling

There are 3 ways to manage if and how stopwatches are enabled.

1) At the group level

As we have seen above, you can enable/disable stopwatches at the group level,

// the default group
Stopwatch.enabled = true

// or if you created your own group
myStopwatchGroup.enabled = false

By default, groups are disabled. You must explicitly enable them to monitor your application. Also, the group’s enabled property overrides stopwatches’ enabled property (see below).

2) “On demand”

If a group is enabled and a new stopwatch name is used, the enableOnDemand property controls whether the new stopwatch is automatically enabled,

// the default group
Stopwatch.enableOnDemand = true

// or if you created your own group
myStopwatchGroup.enableOnDemand = false

// when enableOnDemand is false,
// you must explicitly enable stopwatches
myStopwatchGroup.enable("someCode")

Otherwise, non-preinitialized stopwatches are disabled by default. If enableOnDemand is false, you must explicitly enable stopwatches (see below).

3) Individual stopwatches

In addition to enabling/disabling at the group level, you can also enable and disable individual stopwatches explicitly,

// the default group
Stopwatch.enable("myCode")
// or...
Stopwatch.disable("myCode")

// if you created your own group
myStopwatchGroup.enable("myCode")
// or ...
myStopwatchGroup.disable("myCode")

Time Distribution

You can define a time distribution range on stopwatch groups to plot execution time distribution graphs.

import stopwatch.Stopwatch
import stopwatch.StopwatchRange
import stopwatch.TimeUnit._

// somewhere in your code, before using the stopwatch
// distribution:  0s to 15s in 500ms intervals
Stopwatch.range = StopwatchRange(0 seconds, 15 seconds, 500 millis)

Resetting statistics

You may programmatically reset the values of stopwatches if your application has multiple cycles or if you want to recalibrate after the JVM (“HotSpot compiler”) has had time to optimize your code.

import stopwatch.Stopwatch

Stopwatch.reset("test")

Displaying statistics

The “low-tech” way to display statistics is to use one of the toString methods: toShortString, toMediumString, toLongString. This can be useful if you want to log statistics directly to the Console or through an existing logging frameworks, such as Log4J.

scala> val stats = Stopwatch.snapshot("test")

// summary only
scala> s.toShortString
res3: String = Stopwatch "test" {hit=2, min=1838ms, avg=2148ms, max=2457ms, total=4296ms, stdDev=437ms}


// includes thread info
scala> s.toMediumString
res4: String = Stopwatch "test" {hits=2, throughput=0.814/s, minTime=1838ms, avgTime=2148ms, maxTime=2457ms, totalTime=4296ms, stdDev=437ms, currentThreads=0, avgThreads=1.50, maxThreads=2, first=2009-12-21 11:22:54.708 PST, last=2009-12-21 11:22:57.166 PST}


// includes threads + time distribution
scala> s.toLongString
res5: String = Stopwatch "test" {hits=2, throughput=0.814/s, minTime=1838ms, avgTime=2148ms, maxTime=2457ms, totalTime=4296ms, stdDev=437ms, currentThreads=0, avgThreads=1.50, maxThreads=2, first=2009-12-21 11:22:54.708 PST, last=2009-12-21 11:22:57.166 PST} Distribution {under=0, 0-1000ms: 0, 1000-2000ms: 1, 2000-3000ms: 1, 3000-4000ms: 0, 4000-5000ms: 0, 5000-6000ms: 0, 6000-7000ms: 0, 7000-8000ms: 0, 8000-9000ms: 0, 9000-10000ms: 0, over=0}

The standard toString method is equivalent to toMediumString.

You can also use the StopwatchStatistic interface to obtain only the information you need and display it differently.

Web Interface

The optional stopwatch-web.jar library provides a small embedded web server to monitor your application in a development/certification setting. It does not yet provide any security beyond only opening “localhost” loopback-only port.

You can embed the server by inserting the following code fragment in your application’s startup code:

import stopwatch.web.Server

val server = new stopwatch.web.Server

// register StopwatchGroups you want to monitor
server.groups ::= Stopwatch
server.groups ::= myStopwatchGruop

// configure port number
server.port = 9999

server.start()

Try it!

You can try a sample of the web server by running,

% scala -cp stopwatch-1.x.jar:stopwatch-web-1.x.jar stopwatch.web.SampleServer

If you point your browser to http://localhost:9999, you should see:

and by clicking on the distribution sparklines, you will see more details about the distribution range:

TODO’s

Target platform

Stopwatch does not rely on any 3rd-party library dependencies.

Mailing list

Questions? Comments? Join, browse or search the mailing list

If you are a member of the mailing list, you can send messages directly to scala-stopwatch@googlegroups.com

Version History

Version 1.0-SNAPSHOT

Download

Download jar files directly from the Maven2 repository.

Source Code

The official Github repository is at http://github.com/aboisvert/stopwatch.

Refer to the README.md for build instructions.

License

Stopwatch is is licensed under the terms of the Apache Software License v2.0