This blog post is part of a series on non functional requirements, and how they take most of the effort.
The scenario
You want a third party to implement an application package to allow people to buy and sell widgets from their phone. Once the package has been developed, they will hand it over to you to sell, support, maintain and upgrade and you will be responsible for it,
At the back-end is a web server.
Requirements you have been given.
- We expect this application package to be used by all the major banks in the world.
- For the UK we expect the number of people who have an account to be about 10 million people
- We expect about 1 million trades a day.
See start here for additional topics.
Why plan for debug information?
The web server will be installed in customer environments, to which you do not have access. If there is are problems the customer will expect you to diagnose the problem with the information the product provides.
If you turn the trace on for some products it uses up much more CPU, and this may be unacceptable to a customer ( Quote from an upset customer “Are you saying I have to buy a bigger server just to collect the trace!?!”)
Even with a low transaction rate of 100 a second, running with a verbose trace will be very expensive, and there will be a lot of it.
Some products write an entry and exit trace. You could be smart, do not create an entry trace, and only provide an exit trace if there is an unexpected or interesting condition. In this case you need to write the input parameters, and error codes, and enough information to be able to identify the problem. This might include an account number, a database table name etc.
You might want to write trace in a binary format and format it when needed – this saves the CPU used to format the data, but it is more work to write code to format the data.
Every unexpected return code from a function should be traced. If you are doing a database call, and it returns no record found – this may be expected, and so can be ignored. If you were doing a database update, and you get record not found – this is a problem.
To help with the flow through some code, you could consider footprints in storage. You have macro increments a counter, and sets on a bit in the storage. For example
bit debugInfo[60];
...
debugInfo[1] = 1;
if ()
{
debugInfo[2] = 1;
}
else
{
debugInfo[3] = 1;
}
if ( writing_trace_entry)
{
output debuginfo...
}
and you have a clue as to the path taken through the code.
With every trace entry provide the information needed to identify which source module, and which point within the source module. This could be module name, and line number, or combination of a number for the source (myprog.c has number 73) and the n’th trace macro instance.
You might want to be able to trace an individual user, such as account number… This has a much smaller impact to tracing all systems.
You might want trace a component, such as database, TLS, authentication etc.