Instrumenting your code to report application level metrics is definitely one of the most powerful monitoring tasks you can accomplish. It is damn satisfying to get working the first time as well. Having the ability to look at your application and how it is performing at a granular level can help identify potential issues or bottlenecks but can also give you a greater understanding of how people are interacting with the application at a broad scale. Everybody loves having these types of metrics to talk about their apps and products so this style of monitoring is a great win for the whole team.
I don’t want to dive in to the specifics of WHAT you should monitor here, that will be unique to every environment. Instead of covering the what and how of instrumenting the code to report specific metrics, I will be running through an example of what the process might look like for instrumenting a check and alarm for monitoring and alerting purposes at an operations level. I am not a developer, so I don’t spend a lot of time thinking about what types of things are important to collect metrics on. Usually my job instead is to figure out how to monitor and alert effectively, based on the metrics that developers come up with.
Sensu has a great plugin to check Graphite thresholds in their plugin repo. If you haven’t looked already, take a minute to glance over the options a little bit and see how the plugin works. It is a pretty simple plugin but has been able to do everything I need it to.
One common monitoring task is to check how long requests are taking. So in this example, we are querying the Graphite server and reporting a critical status (status 1) if the request averages more than 7 seconds for a response time.
Here is the command you would run manually to check this threshold. Make sure to download the script if you haven’t already, you can just copy the code directly or clone the repo if you are doing this manually. If you are using Sensu you can use the sensu_plugin LWRP to grab the script (more on that below).
./check-data -s <servername:port> -t <graphite query> -c 7000 -u user -p password ./check-data -s graphite.example.com -t alias(stats.timer.server.response_time.mean, 'Mean') -c 7000 -u myuser -p awesomepassword
There are a few things to note. The -s flag specifies which graphite server or endpoint to hit, -t specifies the target or the graphite query to run the script against, the -c flag sets the threshold, -u and -p are used if your Graphite server uses authentication. If your Graphite instance is public it should probably use auth, otherwise if it is internal only, probably not as important. Obviously these are just dummy values, included to give you a better idea of what a real command should look like. Use your own values in their place.
The query we’re running is against a statsd metric that for mean response time for a request that gets recorded from the code (this is the developer instrumenting their code part I mentioned). This check is specific to my environment so you will need to modify any of your queries to make sure to alert on a useful metric and threshold in your own environment.
Here’s an example of what the graphite graph (rendered in Grafana) looks like.
Obviously this is just a sample but it should give you the general idea of what to look for.
If you examine the script, there are a few Ruby Gem requirements to get the script to run, which you will need to be installed if you haven’t already. They are sensu-plugin, json, json-uri and openssl. You don’t need the sensu-plugin if you are just running the check manually but you WILL need to have it installed on the Sensu client that will be running the scheduled check. That can be done manually or with the Sensu Chef recipe (specifically for turning on Sensu embedded ruby and ruby gems), which I recommend using anyway if you plan on doing any type of deployments at scale using Sensu.
Here is the Chef code looks like if you use Sensu to deploy this check automatically.
sensu_check "check_request_time" do command "#{node['sensu']['plugindir']}/check-data.rb -s graphite.example.com -t \"alias(stats.timers.server.facedetection.response_time.mean, 'Mean')\" -c 7000 -a 360 -u myuser -p awesomepassword" handlers ["pagerduty", "slack"] subscribers ["core"] interval 60 standalone true additional(:notification => "Request time above threshold", :occurrences => 5) end
This should look familiar if you have any background using the Sensu Chef cookbook. Basically we are using the sensu_check LWRP to execute the script with the different parameters we want, using the pagerduty and slack handlers, which are just fancy ways to pipe out the results of the check. We are also saying we want to run this on a scheduled interval time of 60 seconds as a standalone check, which means it will be executed on the client node (not the Sensu server itself). Finally, we are saying that after 5 failed checks we want to append a message to the handler that says what exactly is going wrong.
You can stick this logic in an existing recipe or create a new one that handles your metrics threshold checks. I’m not sure what the best practice is for where to put the check but I have a recipe that runs standalone threshold checks that I stuck this logic in to and it seems to work. Once the logic has been added you should be able to run chef-client for the new check to get picked up.