Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[fix/mashape-analytics] ALF serializer and Buffer #425

Merged
merged 8 commits into from
Jul 29, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ TESTING_CONF = kong_TEST.yml
DEVELOPMENT_CONF = kong_DEVELOPMENT.yml
DEV_ROCKS=busted luacov luacov-coveralls luacheck

.PHONY: install dev clean start seed drop lint test test-integration test-plugins test-all coverage
.PHONY: install dev clean start restart seed drop lint test test-integration test-plugins test-all coverage

install:
@if [ `uname` = "Darwin" ]; then \
Expand Down Expand Up @@ -38,6 +38,9 @@ start:
stop:
@bin/kong stop -c $(DEVELOPMENT_CONF)

restart:
@bin/kong restart -c $(DEVELOPMENT_CONF)

seed:
@bin/kong db -c $(DEVELOPMENT_CONF) seed

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Kong was created to secure, manage and extend Microservices & APIs. Kong is powe
- **Scalability**: Distributed by nature, Kong scales horizontally simply by adding nodes.
- **Performance**: Kong handles load with ease by scaling and using NGINX at the core.
- **Plugins**: Extendable architecture for adding functionality to Kong and APIs.
- **OAuth2.0**: Add easily an OAuth2.0 authentication to your APIs.
- **OAuth2.0**: Add easily an OAuth2.0 authentication to your APIs.
- **Logging**: Log requests and responses to your system over HTTP, TCP, UDP or to disk.
- **IP-restriction**: Whitelist or blacklist IPs that can make requests.
- **Analytics**: Visualize, Inspect and Monitor API traffic with [Mashape Analytics](https://apianalytics.com).
Expand Down Expand Up @@ -89,11 +89,12 @@ When developing, use the `Makefile` for doing the following operations:
| `dev` | Setup your development environment |
| `clean` | Clean your development environment |
| `start` | Start the `DEVELOPMENT` environment (`kong_DEVELOPMENT.yml`) |
| `restart` | Restart the `DEVELOPMENT` environment (`kong_DEVELOPMENT.yml`) |
| `seed` | Seed the `DEVELOPMENT` environment (`kong_DEVELOPMENT.yml`) |
| `drop` | Drop the `DEVELOPMENT` environment (`kong_DEVELOPMENT.yml`) |
| `lint` | Lint Lua files in `kong/` and `spec/` |
| `test` | Run the unit tests |
| `test-integration | Run the integration tests (Kong + DAO) |
| `test-integration | Run the integration tests (Kong + DAO) |
| `test-plugins | Run unit + integration tests of all plugins |
| `test-all` | Run all unit + integration tests at once |
| `coverage` | Run all tests + coverage report |
Expand Down
2 changes: 1 addition & 1 deletion kong/dao/cassandra/apis.lua
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ function Apis:delete(where_t)
return ok
end

return { apis = Apis }
return {apis = Apis}
110 changes: 52 additions & 58 deletions kong/plugins/log_serializers/alf.lua
Original file line number Diff line number Diff line change
@@ -1,49 +1,24 @@
-- ALF serializer module.
-- ALF is the format supported by Mashape Analytics (http://apianalytics.com)
--
-- This module represents _one_ ALF entry, which can have multiple requests entries.
-- This module represents _one_ ALF, zhich has _one_ ALF entry.
-- It used to be a representation of one ALF with several entries, but ALF
-- had its `clientIPAddress` moved to the root level of ALF, hence breaking
-- this implementation.
--
-- # Usage:
--
-- ## Create the ALF like so:
-- local alf = ALFSerializer:new_alf()
--
-- ## Add entries:
-- local n_entries = alf:add_entry(ngx)
--
-- ## Output the ALF with all its entries as JSON:
-- local json_str = alf:to_json_str(service_token)
-- local alf = ALFSerializer:new_alf(ngx, serviceToken, environment)
--
-- - ALF specifications: https://github.com/Mashape/api-log-format
-- - Nginx lua module documentation: http://wiki.nginx.org/HttpLuaModule
-- - ngx_http_core_module: http://wiki.nginx.org/HttpCoreModule#.24http_HEADER

local json = require "cjson"
local stringy = require "stringy"

local EMPTY_ARRAY_PLACEHOLDER = "__empty_array_placeholder__"

local alf_mt = {}
alf_mt.__index = alf_mt

function alf_mt:new_alf()
local ALF = {
version = "1.0.0",
serviceToken = "", -- will be filled by to_json_string()
environment = nil, -- stub
har = {
log = {
version = "1.2",
creator = {
name = "mashape-analytics-agent-kong",
version = "1.0.0"
},
entries = {}
}
}
}

return setmetatable(ALF, self)
end

-- Transform a key/value lua table into an array of elements with `name`, `value`.
-- Since Lua won't recognize {} as an empty array but an empty object, we need to force it
-- to be an array, hence we will do "[__empty_array_placeholder__]".
Expand All @@ -61,7 +36,7 @@ local function dic_to_array(hash, fn)
v = {v}
end
for _, val in ipairs(v) do
table.insert(arr, { name = k, value = val })
table.insert(arr, {name = k, value = val})
fn(k, val)
end
end
Expand All @@ -73,19 +48,21 @@ local function dic_to_array(hash, fn)
end
end

local _M = {}

-- Serialize `ngx` into one ALF entry.
-- For performance reasons, it tries to use the NGINX Lua API instead of
-- ngx_http_core_module when possible.
-- Public for unit testing.
function alf_mt:serialize_entry(ngx)
function _M.serialize_entry(ngx)
-- ALF properties computation. Properties prefixed with 'alf_' will belong to the ALF entry.
-- other properties are used to compute the ALF properties.

-- bodies
local analytics_data = ngx.ctx.analytics

local alf_req_body = analytics_data.req_body
local alf_res_body = analytics_data.res_body
local alf_req_body = analytics_data.req_body or ""
local alf_res_body = analytics_data.res_body or ""

-- timers
local proxy_started_at, proxy_ended_at = ngx.ctx.proxy_started_at, ngx.ctx.proxy_ended_at
Expand All @@ -96,7 +73,18 @@ function alf_mt:serialize_entry(ngx)
local alf_send_time = proxy_started_at - alf_started_at * 1000

-- Time waiting for the upstream response
local alf_wait_time = ngx.var.upstream_response_time * 1000
local upstream_response_time = 0
local upstream_response_times = ngx.var.upstream_response_time
if not upstream_response_times or upstream_response_times == "-" then
-- client aborted the request
return
end

upstream_response_times = stringy.split(upstream_response_times, ", ")
for _, val in ipairs(upstream_response_times) do
upstream_response_time = upstream_response_time + val
end
local alf_wait_time = upstream_response_time * 1000

-- upstream response fully received - upstream response 1 byte received
local alf_receive_time = analytics_data.response_received and analytics_data.response_received - proxy_ended_at or -1
Expand Down Expand Up @@ -126,7 +114,6 @@ function alf_mt:serialize_entry(ngx)

return {
startedDateTime = os.date("!%Y-%m-%dT%TZ", alf_started_at),
clientIPAddress = ngx.var.remote_addr,
time = alf_time,
request = {
method = ngx.req.get_method(),
Expand All @@ -140,7 +127,7 @@ function alf_mt:serialize_entry(ngx)
postData = {
mimeType = alf_req_mimeType,
params = dic_to_array(ngx.req.get_post_args()),
text = alf_req_body and alf_req_body or ""
text = alf_req_body
}
},
response = {
Expand All @@ -155,7 +142,7 @@ function alf_mt:serialize_entry(ngx)
content = {
size = tonumber(ngx.var.body_bytes_sent),
mimeType = alf_res_mimeType,
text = alf_res_body and alf_res_body or ""
text = alf_res_body
}
},
cache = {},
Expand All @@ -171,27 +158,34 @@ function alf_mt:serialize_entry(ngx)
} -- end of entry
end

function alf_mt:add_entry(ngx)
table.insert(self.har.log.entries, self:serialize_entry(ngx))
return table.getn(self.har.log.entries)
end

function alf_mt:to_json_string(token, environment)
if not token then
function _M.new_alf(ngx, token, environment)
if not ngx then
error("Missing ngx context", 2)
elseif not token then
error("Mashape Analytics serviceToken required", 2)
end

-- inject token
self.serviceToken = token
-- inject environment (left empty if nil)
self.environment = environment

local str = json.encode(self)
return str:gsub("\""..EMPTY_ARRAY_PLACEHOLDER.."\"", ""):gsub("\\/", "/")
end
local entry = _M.serialize_entry(ngx)
if not entry then
return
end

function alf_mt:flush_entries()
self.har.log.entries = {}
return {
version = "1.0.0",
serviceToken = token,
environment = environment,
clientIPAddress = ngx.var.remote_addr,
har = {
log = {
version = "1.2",
creator = {
name = "mashape-analytics-agent-kong",
version = "1.0.1"
},
entries = {_M.serialize_entry(ngx)}
}
}
}
end

return alf_mt
return _M
Loading