Martin Redington
Samples
Collecting user feedback is a necessary part of the software development life-cycle. In an ideal world, this feedback would consist solely of praise, gratitude, and PayPal donations.
Sadly, much, if not most, user feedback consists of problem or bug reports of one kind of another. No matter how thorough your testing process, some problems will only appear once your software in the world in the hands of real users.
In the Mac world, developers usually provide an email address or a web form for users to submit initial problem reports.
Having received the initial report, the developer can perform an initial diagnosis of the underlying issue, and whether it is already known, or not.
In many cases however, further information is required to accurately
diagnose the problem. The initial report may not contain basic
information (such as the application version number or operating
system version), or slightly more esoteric information may be needed
for specific diagnosis, such as the users' network configuration (as
shown by the output of running /sbin/ifconfig -a in the
Terminal).
The flow diagram below shows a characterisation of this process.
This process can relatively intensive, both for the developer, and the end-user. Explaining what information is required, and gathering that information, especially where the user is relatively technically unsophisticated, can be especially time-consuming, and in many cases, users, or developers, can simply lose interest in pursuing the issue, or simply get distracted, especially where the application is not critical for the user.
How could we improve the problem reporting and management process?
Anything we can do to increase the quality and information content of the initial report sent by the user, and to reduce the overhead of asking for (from the support perspective) and providing (from the end-user perspective) further diagnostic information would reduce the burden on, and cost to, both parties.
Interestingly, Apple does provide some automated problem reporting facilities as part of the Mac OS X operating system.
A process called crashreporterd watches the signals that
are sent to running applications, and when a signal indicates an
application crash, launches the CrashReporter. This
collects a crash log, which contains stack traces and other technical
information, as well as a profile of the system, and a user provided
description of the circumstances leading to the crash, for delivery
back to Apple.
This has a few disadvantages:
Reports are sent to Apple (who generally does not pass them on), rather than to the application developer.
Reports are only generated when a crash occurs. While all crashes are bugs, not all bugs lead to crashes.
The information provided is fixed, and relatively limited.
Users never receive any replies to these reports, and even if users send them initially, they will tend to stop sending them after a while.
A number of people have written their own CrashReporters to address the first of these issues.
The most well-known example is Insanity's Smart Crash Reports (see ZonicBugReporter or ILCrashReporter for other examples).
Although these alternative do at least send the report to the application developer, they are generally reimplementations of Apple's CrashReporter with a different delivery address, and still suffer from its remaining shortcomings.
What if we had a CrashReporter that could be easily configured to collect a wider range of information, according to the application developer's specific requirements, in response both to crashes, and user-initiated bug reports, and that also maintained a channel back to the user who originated the report, that could be used to follow up the problem?
MMICrashReporter aims to fill this gap.
MMICrashReporter is a second (or third) generation crash reporter, with the following features:
Powerful information gathering capabilities: MMICrashReporter can collect contact information, user description, contact details, crash logs, log files, preferences, and the output of arbitrary commands.
High configurability: The information to be collected, and the user interface to be presented to the user can be configured via an XML configuration file.
XML property list output: to allow easy automatic processing of reports.
Extensible data collection architecture: If the existing data collection components do not meet your requirements, you can write your own.
Extensible report delivery architecture: Basic mail and web based delivery are included, but you can also write your own delivery agent plugins, if necessary.
"BugReporter" mode: For collecting non-crashing bug reports in a standardised format.
"Personal Reports": double clicking on a crash reporter configuration file will open a bug report using that configuration. Personalised Configuration files can be sent to users to collect information specific to their problem.
Document-Based: Multiple bug/crash reports can be handled simultaneously.
Delayed delivery: Reports can be saved for later delivery (e.g. when the user is offline).
Fully localizable: Ask users for bug reports in their native language.
Consider an example application, called MMIExampleApp.
We would like to receive both crash reports and normal bug reports for MMIExampleApp.
The default configuration of MMICrashReporter allows us to collect crash reports when MMIExampleApp crashes, and to connect an action in MMIExampleApp's user interface (e.g. a Report Bug item in the Help menu) with bug report collection. In either case, MMICrashReporter will be launched to present the report, and to collect any additional user input.
The default MMICrashReporter crash report looks like this:
This is almost identical to a traditional crash report, except that the user's name and email address are present (these were pre-filled from the user's account setting and Address Book entry). This allows the developer to follow up the problem with the user, and ask for more further diagnostic information if required.
The default MMICrashReporter bug report looks like this:
This is identical to the crash report, except that no crash report field is present.
Already, we have some advantages over current CrashReporters, but in this case of MMIExampleApp, we would also like to collect some additional information.
When MMIExampleApp crashes, we would like to obtain any relevant console log entries.
We simply add a new report item to MMICrashReporter's XML property list configuration, like so:
<dict>
<key>name</key>
<string>ConsoleLogEntries</string>
<key>label</key>
<string>Console Log Entries:</string>
<key>type</key>
<string>ConsoleLog</string>
<key>description</key>
<string>Console Log Entries for MMIExampleApp.</string>
<key>pattern</key>
<string>MMIExampleApp</string>
<key>fixedHeight</key>
<true/>
</dict>
The screenshot below shows the resulting crash report window:
In the case of bug reports, we would like to provide a summary field, where the user will hopefully give us a quick idea of the nature of the problem, and a severity field, so that we can prioritise our response to the report (presumably user's will expect, or at least prefer, a faster response to more serious bugs, and will not mind waiting a bit longer for a response to a minor or trivial issue).
Note that these are just examples. You can configure MMICrashReporter however you like, using either the built-in ReportItems, or if they are insufficient, you can extend MMICrashReporter by writing your own ReportItem plug-ins.
The XML for the new fields is shown below:
<dict>
<key>name</key>
<string>Summary</string>
<key>label</key>
<string>Summary:</string>
<key>type</key>
<string>TextField</string>
<key>description</key>
<string>Please enter a one line summary of your problem here.</string>
</dict>
<dict>
<key>name</key>
<string>Severity</string>
<key>label</key>
<string>Severity:</string>
<key>type</key>
<string>Select</string>
<key>description</key>
<string>Please indicate the severity of the bug. Major indicates a crash, data loss or serious problem. Minor indicates something that doesn't interfere with main functionality, but is still a bug. Trivial indicates a minor annoyance.</string>
<key>options</key>
<array>
<string>Unclassified</string>
<string>Major</string>
<string>Minor</string>
<string>Trivial</string>
</array>
<key>selected</key>
<string>Unclassified</string>
</dict>
The resulting bug report window is shown here:
We have now tailored the information in our initial crash or bug reports to our requirements. Imagine that a problem report has been received, but that further information is required from the user, in order to diagnose the issue.
One possibility is for us to email the user:
From: support@mildmanneredindustries.com
To: user@somewhere.com
Subject: Your problem
Dear user,
in order to diagnose your problem, I need some more information. Could you do the following for me:
In the Terminal, run
/sbin/ifconfig -a
and cut and paste the output into a mail
Then run
defaults read com.mildmanneredindustries.MMIExampleApp
and send me the output.
Finally, could you send me any MMIExample app entries from the console.log
if you run
grep MMIEXampleApp /Library/Logs/Console/`id -u`/console.log
in the Terminal, and send me the output
Thanks very much for your help
MildMannered Industries Support
Many users will fall at this hurdle. Alternatively, using MMICrashReporter, we could send them a slightly different email.
From: support@mildmanneredindustries.com
To: user@somewhere.com
Subject: Your problem
Dear user,
in order to diagnose your problem, I need some more information. Could you double click on the attached file.
A customised bug report will open, and automatically collect the information I need. Just click send when its done.
Thanks very much for your help
MildMannered Industries Support
Attachment: CustomReport.crashreporterconfig
Here is the custom report:
As well as the information mentioned in the first email, this report also contains a bug id. This could be a reference to an identifier in the developer's bug tracker, allowing the response to be easily (or ideally, automatically) added to the bug tracker case.
When the user hits send on any of the reports, MMICrashReporter will attempt to send the report via a delivery agent, to the address specified in the configuration (you can specify different addresses for each mode, if you wish). Mail and http delivery agents are built-in.
The actual report output format is also XML property-list based. Here is a sample output file. Although this format is not especially person friendly, it is very suitable for automated processing.
Possibilities include using XSLT to transform the output into a suitable format for viewing in a browser, or to inject it directly into an issue tracker, or a specialised Cocoa browser. It is likely that support for some or all of the options will appear in future distributions of MMICrashReporter.
The current XML output also contains the configuration parameters of each report item. The intention is that these can be used to help a report viewer construct a user interface without access to the original report configuration. The exact output format is subject to change.
The source and binary for the sample application MMIExampleApp is included in the distribution. Further detail is available here
A number of sample configuration files are also provided - you can launch MMICrashReporter with a new bug report by double clicking on these (if you hold down the option key at the same time, you will get a crash report instead).
Or you could check out the documentation on integration and configuration.