Skip to content

Commit

Permalink
feat(plugins) new Runscope plugin
Browse files Browse the repository at this point in the history
Signed-off-by: Thibault Charbonnier <[email protected]>
  • Loading branch information
mansilladev authored and thibaultcha committed Feb 5, 2016
1 parent 4f01a95 commit 252b4a9
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 0 deletions.
5 changes: 5 additions & 0 deletions kong-0.6.1-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ build = {

["kong.plugins.log-serializers.basic"] = "kong/plugins/log-serializers/basic.lua",
["kong.plugins.log-serializers.alf"] = "kong/plugins/log-serializers/alf.lua",
["kong.plugins.log-serializers.runscope"] = "kong/plugins/log-serializers/runscope.lua",

["kong.plugins.tcp-log.handler"] = "kong/plugins/tcp-log/handler.lua",
["kong.plugins.tcp-log.schema"] = "kong/plugins/tcp-log/schema.lua",
Expand All @@ -156,6 +157,10 @@ build = {
["kong.plugins.file-log.handler"] = "kong/plugins/file-log/handler.lua",
["kong.plugins.file-log.schema"] = "kong/plugins/file-log/schema.lua",

["kong.plugins.runscope.handler"] = "kong/plugins/runscope/handler.lua",
["kong.plugins.runscope.schema"] = "kong/plugins/runscope/schema.lua",
["kong.plugins.runscope.buffer"] = "kong/plugins/runscope/log.lua",

["kong.plugins.mashape-analytics.schema.migrations"] = "kong/plugins/mashape-analytics/schema/migrations.lua",
["kong.plugins.mashape-analytics.handler"] = "kong/plugins/mashape-analytics/handler.lua",
["kong.plugins.mashape-analytics.schema"] = "kong/plugins/mashape-analytics/schema.lua",
Expand Down
32 changes: 32 additions & 0 deletions kong/plugins/log-serializers/runscope.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
local ngx_now = ngx.now
local req_get_method = ngx.req.get_method
local req_start_time = ngx.req.start_time
local req_get_headers = ngx.req.get_headers
local res_get_headers = ngx.resp.get_headers

local _M = {}

function _M.serialize(ngx)
local runscope_ctx = ngx.ctx.runscope or {}

return {
request = {
url = ngx.var.scheme.."://"..ngx.var.host..":"..ngx.var.server_port..ngx.var.request_uri,
method = req_get_method(),
headers = req_get_headers(),
body = runscope_ctx.req_body,
timestamp = req_start_time(),
form = runscope_ctx.req_post_args
},
response = {
status = ngx.status,
headers = res_get_headers(),
size_bytes = ngx.var.body_bytes_sent,
body = runscope_ctx.res_body,
timestamp = ngx_now(),
response_time = ngx.var.request_time * 1
}
}
end

return _M
74 changes: 74 additions & 0 deletions kong/plugins/runscope/handler.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
local runscope_serializer = require "kong.plugins.log-serializers.runscope"
local BasePlugin = require "kong.plugins.base_plugin"
local log = require "kong.plugins.runscope.log"

local ngx_log = ngx.log
local ngx_log_ERR = ngx.ERR
local string_find = string.find
local req_read_body = ngx.req.read_body
local req_get_headers = ngx.req.get_headers
local req_get_body_data = ngx.req.get_body_data
local req_get_post_args = ngx.req.get_post_args
local pcall = pcall

local RunscopeLogHandler = BasePlugin:extend()

function RunscopeLogHandler:new()
RunscopeLogHandler.super.new(self, "runscope")
end

function RunscopeLogHandler:access(conf)
RunscopeLogHandler.super.access(self)

local req_body, res_body = "", ""
local req_post_args = {}

if conf.log_body then
req_read_body()
req_body = req_get_body_data()

local headers = req_get_headers()
local content_type = headers["content-type"]
if content_type and string_find(content_type:lower(), "application/x-www-form-urlencoded", nil, true) then
local status, res = pcall(req_get_post_args)
if not status then
if res == "requesty body in temp file not supported" then
ngx_log(ngx_log_ERR, "[runscope] cannot read request body from temporary file. Try increasing the client_body_buffer_size directive.")
else
ngx_log(ngx_log_ERR, res)
end
else
req_post_args = res
end
end
end

-- keep in memory the bodies for this request
ngx.ctx.runscope = {
req_body = req_body,
res_body = res_body,
req_post_args = req_post_args
}
end

function RunscopeLogHandler:body_filter(conf)
RunscopeLogHandler.super.body_filter(self)

if conf.log_body then
local chunk = ngx.arg[1]
local runscope_data = ngx.ctx.runscope or {res_body = ""} -- minimize the number of calls to ngx.ctx while fallbacking on default value
runscope_data.res_body = runscope_data.res_body..chunk
ngx.ctx.runscope = runscope_data
end
end

function RunscopeLogHandler:log(conf)
RunscopeLogHandler.super.log(self)

local message = runscope_serializer.serialize(ngx)
log.execute(conf, message)
end

RunscopeLogHandler.PRIORITY = 1

return RunscopeLogHandler
94 changes: 94 additions & 0 deletions kong/plugins/runscope/log.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
local cjson = require "cjson"
local url = require "socket.url"

local _M = {}

local HTTPS = "https"
local ngx_log = ngx.log
local ngx_log_ERR = ngx.ERR
local ngx_timer_at = ngx.timer.at
local string_format = string.format
local string_len = string.len

-- Generates http payload .
-- @param `method` http method to be used to send data
-- @param `parsed_url` contains the host details
-- @param `message` Message to be logged
-- @return `payload` http payload
local function generate_post_payload(parsed_url, access_token, message)
local body = cjson.encode(message)
local payload = string_format(
"%s %s HTTP/1.1\r\nHost: %s\r\nConnection: Keep-Alive\r\nAuthorization: Bearer %s\r\nContent-Type: application/json\r\nContent-Length: %s\r\n\r\n%s",
"POST", parsed_url.path, parsed_url.host, access_token, string_len(body), body)
return payload
end

-- Parse host url
-- @param `url` host url
-- @return `parsed_url` a table with host details like domain name, port, path etc
local function parse_url(host_url)
local parsed_url = url.parse(host_url)
if not parsed_url.port then
if parsed_url.scheme == "http" then
parsed_url.port = 80
elseif parsed_url.scheme == HTTPS then
parsed_url.port = 443
end
end
if not parsed_url.path then
parsed_url.path = "/"
end
return parsed_url
end

-- Log to a Http end point.
-- @param `premature`
-- @param `conf` Configuration table, holds http endpoint details
-- @param `message` Message to be logged
local function log(premature, conf, message)
if premature then
return
end

local ok, err
local parsed_url = parse_url(conf.api_endpoint.."/buckets/"..conf.bucket_key.."/messages")
local access_token = conf.access_token
local host = parsed_url.host
local port = tonumber(parsed_url.port)

local sock = ngx.socket.tcp()
sock:settimeout(conf.timeout)

ok, err = sock:connect(host, port)
if not ok then
ngx_log(ngx_log_ERR, "[runscope] failed to connect to "..host..":"..tostring(port)..": ", err)
return
end

if parsed_url.scheme == HTTPS then
local _, err = sock:sslhandshake(true, host, false)
if err then
ngx_log(ngx_log_ERR, "[runscope] failed to do SSL handshake with "..host..":"..tostring(port)..": ", err)
end
end

ok, err = sock:send(generate_post_payload(parsed_url, access_token, message).."\r\n")
if not ok then
ngx_log(ngx_log_ERR, "[runscope] failed to send data to "..host..":"..tostring(port)..": ", err)
end

ok, err = sock:setkeepalive(conf.keepalive)
if not ok then
ngx_log(ngx_log_ERR, "[runscope] failed to keepalive to "..host..":"..tostring(port)..": ", err)
return
end
end

function _M.execute(conf, message)
local ok, err = ngx_timer_at(0, log, conf, message)
if not ok then
ngx_log(ngx_log_ERR, "[runscope] failed to create timer: ", err)
end
end

return _M
10 changes: 10 additions & 0 deletions kong/plugins/runscope/schema.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
return {
fields = {
api_endpoint = {required = true, type = "url", default = "https://api.runscope.com"},
bucket_key = {required = true, type = "string"},
access_token = {required = true, default = "", type = "string"},
timeout = {default = 10000, type = "number"},
keepalive = {default = 30, type = "number"},
log_body = {default = false, type = "boolean"}
}
}

0 comments on commit 252b4a9

Please sign in to comment.