APIs in Web and mobile applications are the rising trend today. One impact of this trend is that a huge amount of commerce today takes place through APIs. These APIs eventually add to your business value.
Well, API performance can directly affect a business, hurting brand image or cutting into revenue as customers struggle to complete their action. What if in your website, pages are taking too long to load. What if your mobile app is taking to long time to load data from server? How will you know that your APIs are performing slower? In worst case, It may happen that your server starts taking more and more time and one night at 2:00 a.m - It crashes! Users of the application will be not happy, loss of business to website or application owners..! Surely, It did not happened all of sudden at 2:00 a.m.
So, as APIs become more and more important, we are clearly going to need the some information to determine if all the APIs you are using or providing are working properly and how they performing ? That’s why, we as developer, manager or project stack owner, need to have API response time monitoring in place for live project. Doing this enables us to know how much time our APIs are taking to respond back to request. If it starts taking more time, then developers can figure it out and fix it before it leads to worst case.
I recently configured a real time API response time monitoring in one of our live projects at Aubergine solutions. In this post, I will be sharing the implementation plan along with configuration of each tool required.
First of all, your API server needs to have logs with response time in it. If you are using nginx, you need to make some changes in nginx.conf file define a new log format timed_combined which captures some additional data in your logs with response time:
log_format timed_combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time $pipe';
In my setup, I have django application running with nginx and uwsgi. I have used uwsgi log file which has following structure for log.
[pid: 2493|app: 0|req: 24480/48984] 127.0.0.1 () {36 vars in 516 bytes} [Sat May 6 04:50:43 2017] GET /api/users/2/ => generated 1710 bytes in 53 msecs (HTTP/1.1 200) 4 headers in 134 bytes (1 switches on core 0)
Now, let’s have quick introduction to the tools I have used for monitoring setup.
Now, let’s install them.
wget -qO — https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
sudo apt-get install apt-transport-https
echo “deb https://artifacts.elastic.co/packages/5.x/apt stable main” | sudo tee -a /etc/apt/sources.list.d/elastic-5.x.list
sudo apt-get update
sudo apt-get install elasticsearch
sudo update-rc.d elasticsearch defaults 95 10
sudo -i service elasticsearch start
wget -qO — https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
sudo apt-get install apt-transport-https
echo “deb https://artifacts.elastic.co/packages/5.x/apt stable main” | sudo tee -a /etc/apt/sources.list.d/elastic-5.x.list
sudo apt-get update
sudo apt-get install logstash
wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-5.2.2-amd64.deb
sudo dpkg -i filebeat-5.2.2-amd64.deb
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.2.0_amd64.deb
sudo apt-get install -y adduser libfontconfig
sudo dpkg -i grafana_4.2.0_amd64.deb
curl -XPUT 'localhost:9200/api_logs?pretty' -H 'Content-Type: application/json' -d'
{
"settings" : {
"index" : {
"number_of_shards" : 3,
"number_of_replicas" : 2
}
}
}
Change input path to point a valid log file. Config file is located at ‘/etc/filebeat/filebeat.yml’
1filebeat.prospectors:
2- input_type: log
3 paths:
4 — /var/log/uwsgi.log
Change name option. It can be used to filter logs based on their source.
Enable output to logstash by removing comment. Replace Ip address with logstash server’s ip-address. Make sure that logstash server is listening to 5044 port from api server.
output.logstash:
hosts: [“xxx.xxx.xxx.xxx:5044”]
input {
…
}
filter {
…
}
output {
…
}
input {
beats{
port => “5044”
}
}
input {
file {
path => “path/to/log/file”
start_position => “beginning”
type => “logs”
}
}
Filters are intermediary processing devices in the Logstash pipeline. You can combine filters with conditionals to perform an action on an event if it meets certain criteria. Some useful filters include:
We are going to parse logs generated by uwsgi. We will use grok to do it. Below is the sample log line for uwsgi logs.
[pid: 2177|app: 0|req: 6/8] 127.0.0.1 () {42 vars in 675 bytes} [Fri Mar 3 05:20:00 2017] GET /api/users/3/ => generated 669 bytes in 16 msecs (HTTP/1.1 200) 4 headers in 134 bytes (2 switches on core 0)
We need to write following code in filter block in order to parse above line.
1filter {
2 grok{
3 match=>{
4 “message”=>”[pid: %{NUMBER:pid}|app: %{NUMBER:id}|req: %{NUMBER:currentReq}/%{NUMBER:totalReq}] %{IP:remoteAddr} (%{WORD:remoteUser}?) {%{NUMBER:CGIVar} vars in %{NUMBER:CGISize} bytes} %{SYSLOG5424SD:timestamp} %{WORD:method} %{URIPATHPARAM:uri} => generated %{NUMBER:resSize} bytes in %{NUMBER:resTime} msecs (HTTP/%{NUMBER:httpVer} %{NUMBER:status}) %{NUMBER:headers} headers in %{NUMBER:headersSize} bytes %{GREEDYDATA:coreInfo}”
5}
6
7 add_field=>{
8 “logtype”=>”api_request”
9 }
10}
11# By default grok will add fields as string, but in order to apply
12# elastic search aggregations, we need to convert data types.
13
14mutate {
15 convert => { “status” => “integer” }
16 convert => { “resTime” => “float” }
17}
18
19# adding extra field “API” to output of this parsing
20# It will help us filter data for this particular API endpoint.
21# similarly, we can add other API endpoint also.
22
23if [uri] =~ /api(/v2)?/users/[0–9]//{
24 mutate{
25 add_field=> [ “API”, “/api/users/<user_id>/”]
26 }
27 }
28}
output {
elasticsearch {
hosts => [ “localhost:9200” ]
index => “api_logs”
}
}
After all configuration is done, you can proceed with starting all of them one by one.
sudo service elasticsearch restart
sudo service logstash start
sudo service filebeat start
Now, make some API calls to your api server and check if they are being parsed successfully and they are stored in elastic-search index.
sudo service grafana-server start
After saving this two graphs you will have something similar to below dashboard.
That’s It. This is how I have configured API response time monitoring.
Also read: Securing Your Elastic Beanstalk Web Application with HTTPS: A Load Balancer-Free Approach