Hosted by Three Crickets

Prudence
Scalable REST Platform
For the JVM

Prudence logo: bullfinch in flight

Debugging

Let's start by admitting that the debugging facilities for dynamic languages on the JVM are lacking: breakpoints are not supported and there are no language-specific runtime inspection tools. However, there are enough other options for debugging in Prudence that you should be able to find all your bugs. Squashing them is up to you!

Live Execution

This immensely powerful (and potentially dangerous) tool allows you to execute arbitrary code in live running applications. There are, of course, endless ways in which you can use this feature for debugging. Below are a few common examples.

Dumping Values

Anything you print to standard output will be returned to you:
curl --data '<% println(application.globals.get("db.connection")) %>' http://localhost:8080/myapp/execute/
You can also dump values to the log:
curl --data '<% application.logger.info(application.globals.get("db.connection")) %>' http://localhost:8080/myapp/execute/

Scheduling Background Tasks

Here's an example of dumping a value to the log every 5 seconds using a background task. First let's create "program.js":
<%
document.require('/prudence/tasks/')
Prudence.Tasks.task({
	fn: function() {
		application.logger.info(application.globals.get('db.connection'))
	},
	repeatEvery: '5s'
})
%>
Then execute it:
curl --data-binary @program.js http://localhost:8080/myapp/execute/

Debug Page

The debug page contains a lot of useful debugging information. It's easy to get it just by causing an exception on purpose:
curl --data '<% throw null %>' http://localhost:8080/myapp/execute/

Logging

Logging is by far the most useful weapon in your debugging arsenal. Learn how to use it and use it well.
Some programming languages—Python and Ruby—have their own standard logging mechanisms, which you are welcome to use if you prefer. However, consider using Prudence's (actually Sincerity's) logging system instead: it is especially powerful and flexible, and moreover is shared by all programming languages running in Prudence.
The default logging configuration is production-ready: it will use the following rolling log files under your container's "/logs/" directory:

Introduction

It's important to understand that there's a clear separation of concerns between what to log and how to log it. Programming should be concerned only with the what: the use of the API during runtime and the content of messages. Administration should be concerned only with the how: configuration of the logging system, which occurs during bootstrap.
From a programming perspective, you only care about "loggers," the entities through which you send log messages. The programmer doesn't actually know where the log messages are going. (While it is possible to change the logging configuration via API during runtime, it is not a common practice.)
"Appenders" are the mechanisms that actually write/store/send the messages somewhere, whether it's to a file, to a database, or over the network. The API is entirely ignorant as to what appenders are attached to which loggers: appenders are purely a matter of configuration.

API

Loggers are identified by a unique name in your container, and are configured globally.
The logger names are hierarchical, whereby the hierarchy is specified using ".". For example, "myApp.system.mail" is a child logger of "myApp.system". A child logger inherits its parent's configuration, which it can then override. Parents inherit from their parents, etc., up to what is called the "root logger," which is the logger with an empty string name.
Every log message you send has a "log level." The lower the level, the more specific the message. Loggers are configured with a minimum level: they will log only messages equal to or above it. So, the lower the minimum log level configured for the logger, the more chatter you should get. The following levels are supported:
application.logger and application.getSubLogger
application.logger gives you access to a unique Logger for the application, with the "prudence." prefix. The name of the logger can be configured in settings.js, and defaults to the name of the application's subdirectory.
The easiest way to use this API is via the methods named after the log levels:
application.logger.info('An info-level message')
application.logger.warning('Something bad happened!')
application.getSubLogger can be used to easily access child loggers of the application.logger:
application.getSubLogger('email').info('An info-level message')
If your application logger name is "cms", then the above logger name would be "prudence.cms.email".
Other Loggers
Instead of using application.logger and application.getSubLogger, you can access the logging API directly to retrieve a logger with any name:
importClass(java.util.logging.Logger)
var logger = Logger.getLogger('myApp.system.mail')
logger.info('My logger!')
/prudence/logging/
If you're using JavaScript, it's recommend that you use this friendly wrapper, which adds many convenient additions over the raw logging API.
Here are examples of simple logging, where you can refer to the log levels using simple strings, or use dedicated methods named after the log levels:
document.require('/prudence/logging/')
var logger = new Prudence.Logging.Logger()
logger.log('info', 'An informational message')
logger.fine('A fine message')
logger.warning('A warning')
new Prudence.Logging.Logger('email').info('Hello from the Email subsystem')
String interpolation is supported for all methods, using /sincerity/templates/:
logger.severe('Hi, {0} and {1}!', 'mom', 'dad')
logger.info('Hi, {name}!', {name: 'mom'})
logger.finest('Hi, {name}!', function(original, key) {
	if (key == 'name') {
		return 'mom'
	}
	return null
})
Quick JSON dump, in short form (single line, condensed) or long form (multi-line, indented):
var x = {test: 'hello', more: [1, 2, 3]}
logger.dumpShort(x, 'Our object in short form')
logger.dumpLong(x, 'Our object in long form')
logger.dump(x, 'This is an alias for dumpShort')
Log exceptions using full or partial stack trace (supports both JavaScript and JVM exceptions):
try {
	nothing()
}
catch (e) {
	logger.exception(e)
}
Also very useful is automatic timing of sections of code, which will emit a message when the function starts, when it ends with the execution duration (either due to successful completion or due to a uncaught exception):
logger.time('The loop', function() {
	var x = 1
	for (var i = 0; i < 100000; i++) {
		x /= 2
	}
})
Performance Considerations
Log messages will not be written if they are configured to be filtered out. However, your code is still constructing the message and sending it to the logging system, which may involve a performance hit.
The easiest way to optimize is to check for the log level before constructing the message:
if (logger.isLoggable(java.util.logging.Level.FINE)) {
	var data = heavyFetchFromDatabase()
	return 'This function was called because the "fine" log level is permitted'
}
If you're using the /prudence/logging/ library, as a shortcut you can send an anonymous function:
logger.fine(function() {
	var data = heavyFetchFromDatabase()
	return 'This function was called because the "fine" log level is permitted'
})
Obviously, this is cumbersome to always follow this format, so it's best used only if constructing the message indeed involves heavy lifting.

Configuration

You configure all your loggers and appenders under your container's "/configuration/logging/" directory, which is handled by Sincerity's logging plugin, so it's recommended you read the documentation there. The plugin has useful add-ons for centralizing logging, for example to a dedicated Log4j server, or directly to MongoDB.
The best way to learn how to configure logging is to go through the default logging configuration. As usual, Prudence and Sincerity use "configuration by script," so in fact you'll be using the /sincerity/log4j/ library.
Configuration Log Levels
In order to maintain conformity, the API used throughout Prudence is the standard JVM logging API (often called JULI: "java.util.logging Interface"). However, the logging system used by default in Prudence is Sincerity's logging plugin funnels several different logging APIs into Apache Log4j.
Unfortunately, until the JVM finally included a logging standard, there were already other popular logging APIs. Also unfortunately, the introduced standard is simply not as powerful as Log4j, and we did not want to compromise on power. Fortunately, the SLF4J library allows Sincerity to mimic all these various APIs while implementing them in Log4j, so you get the best of all worlds as well as a uniform system.
The outcome of this is slightly annoying: the log level names used in the configuration files (Log4j) are different from those used in the API (JULI), and indeed the configuration is coarser than the API. Here's how they match up:
Configuration Log Level API Log Level
fatal
error severe
warn warning
config
info info
debug fine
finer
trace finest
So, configuring a logger's level to "info" will mean that it will log the following API levels: "info," "config," "info," "warning" and "severe."
web.log
To learn how to configure the message format for this special logger, see the log service.
Other Loggers
Various subsystems within Prudence use their own loggers. The default configuration files set these to reasonable defaults, which you can modify:

Quick-and-Dirty Logging to the Console

Sometimes, in the midst of intensive debugging, setting up proper logging is just too time consuming. It would also be wasteful, because you know you will quickly delete the logging message once you find the bug.
In such situations the most straightforward solution is to output your message to the console. Since often Prudence captures standard output (for example, in template resources), you may need to override any captures and go straight to the console:
java.lang.System.out.println('Desperate times call for desperate measures!')
Note that there is no console if you're running Prudence as a service/daemon (it is captured to the wrapper log). In those cases you either want to switch to running Prudence from the console, or use the logging API instead.

JVM Logging

If you prefer, you can use the JVM's built-in logging instead of the Sincerity plugin. It is less flexible and much less scalable, but may be preferable in some cases. To configure it, create a configuration file, for example "logging.properties", and define the properties of the handlers and loggers:
handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler
java.util.logging.FileHandler.pattern = log.%u.%g.txt
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s %4$s: %5$s%n
java.util.logging.ConsoleHandler.level = ALL
com.threecrickets.prudence.cache.level = ALL
.level = INFO
Then, start Prudence using a switch to point at that configuration:
JVM_SWITCHES=-Djava.util.logging.config.file=logging.properties sincerity use mycontainer : start prudence
For more information, see the JVM documentation.

Best Practices

There are many different styles of logging, but here are some good rules of thumb:

Debug Page

Uncaught exceptions will cause Prudence to generate a 500 "internal error" HTTP status. When debug modeis enabled, this will come with a generated HTML page full of useful debugging information:
Obviously, this information may include private data, so you'll want to turn this debug page off for production deployments.

Monitoring

One of the advantages of having Prudence running on the JVM is its powerful built-in monitoring capabilities based on JMX technology. Using the VisualVM tool, you can get a comprehensive view of the JVM's memory use over time, examine garbage collection, threading behavior and performance, and perform basic profiling. It's an invaluable tool for debugging memory leaks and performance bottlenecks.

Threads

Here are a few common thread groups you will see for the Prudence's subsystems:
The "thread dump" button is very useful for getting a picture of what all threads are doing at a particular moment.

Management Beans

If you install the MBeans plugin, you will gain access to useful information and functionality exposed by Prudence's subsystems:

Remote JMX

Connecting to a running Prudence JVM over TCP/IP is not so trivial. See the "extras" section in the Sincerity service plugin documentation for a guide.

Memory Leaks

These are sometimes difficult to debug: by the time you get an OutOfMemoryError, it might be impossible to connect over JMX.
jmap and jhat
You should familiarize yourself with these two powerful heap analysis tools.
Use jmap to get a live dump from a running JVM:
jmap -dump:live,format=b,file=/full/path/to/dumps/prudence.bin <pid>
If you're getting connection errors, try the following:
When opening the dump using jhat, you might get an OutOfMememoryError (again!) if the file is very large. Increase available memory like so:
jhat -J-mx2000m prudence.bin
Point your web browser to http://localhost:7000/ to see jhat's analysis.
More Logging
You can enforce a heap dump to file upon OutOfMemoryError using the following JVM command line switches:
JVM_SWITCHES=\
	-XX:+HeapDumpOnOutOfMemoryError \
	-XX:HeapDumpPath=/full/path/to/logs/ \
	sincerity start prudence
Likewise, it's a good idea to turn on the garbage collector log:
JVM_SWITCHES=\
	-Xloggc:/full/path/to/logs/gc.log \
	-XX:+PrintGCDetails \
	-XX:+PrintTenuringDistribution \
	sincerity start prudence
If you're using the Sincerity service plugin, you can enable both of these features by creating a "/configuration/service/jvm/memory-monitoring.conf" file:
# Out-of-memory heap dumps
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath={sincerity.container.root}/logs/
​
# Garbage collection log
-Xloggc:{sincerity.container.root}/logs/gc.log
-XX:+PrintGCDetails
-XX:+PrintTenuringDistribution

The Prudence Manual is provided for you under the terms of the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. The complete manual is available for download as a PDF.

Download manual as PDF Creative Commons License