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

Http logging #251

Merged
merged 11 commits into from
May 21, 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
4 changes: 4 additions & 0 deletions kong-0.3.0-1.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ build = {
["kong.plugins.udplog.handler"] = "kong/plugins/udplog/handler.lua",
["kong.plugins.udplog.log"] = "kong/plugins/udplog/log.lua",
["kong.plugins.udplog.schema"] = "kong/plugins/udplog/schema.lua",

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

["kong.plugins.filelog.handler"] = "kong/plugins/filelog/handler.lua",
["kong.plugins.filelog.log"] = "kong/plugins/filelog/log.lua",
Expand Down
1 change: 1 addition & 0 deletions kong.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ plugins_available:
- tcplog
- udplog
- filelog
- httplog
- cors
- request_transformer

Expand Down
15 changes: 15 additions & 0 deletions kong/plugins/httplog/handler.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
local BasePlugin = require "kong.plugins.base_plugin"
local log = require "kong.plugins.httplog.log"

local HttpLogHandler = BasePlugin:extend()

function HttpLogHandler:new()
HttpLogHandler.super.new(self, "httplog")
end

function HttpLogHandler:log(conf)
HttpLogHandler.super.log(self)
log.execute(conf)
end

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

local _M = {}

-- 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(method, parsed_url, message)
local body = cjson.encode(message);
local payload = string.format("%s %s HTTP/1.1\r\nHost: %s\r\nConnection: Keep-Alive\r\nContent-Type: application/json\r\nContent-Length: %s\r\n\r\n%s",
method:upper(), parsed_url.path, parsed_url.host, 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)
local ok, err
local parsed_url = parse_url(conf.http_endpoint)
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.ERR, "failed to connect to "..host..":"..tostring(port)..": ", err)
return
end

ok, err = sock:send(generate_post_payload(conf.method, parsed_url, message).."\r\n")
if not ok then
ngx.log(ngx.ERR, "failed to send data to "..host..":"..tostring(port)..": ", err)
end

ok, err = sock:setkeepalive(conf.keepalive)
if not ok then
ngx.log(ngx.ERR, "failed to keepalive to "..host..":"..tostring(port)..": ", err)
return
end
end

function _M.execute(conf)
local ok, err = ngx.timer.at(0, log, conf, ngx.ctx.log_message)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the benefit of putting this into a timer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thibaultcha you don't have access to the cosocket API into the log_by_lua handler, that's why a timer is needed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
end
end

return _M
6 changes: 6 additions & 0 deletions kong/plugins/httplog/schema.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
return {
http_endpoint = { required = true, type = "string" },
method = { default = "POST", enum = { "POST", "PUT", "PATCH" } },
timeout = { default = 10000, type = "number" },
keepalive = { default = 60000, type = "number" }
}
4 changes: 2 additions & 2 deletions spec/integration/cli/start_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ describe("CLI", function()
}
}

replace_conf_property("plugins_available", {"ssl", "keyauth", "basicauth", "tcplog", "udplog", "filelog", "request_transformer"})
replace_conf_property("plugins_available", {"ssl", "keyauth", "basicauth", "tcplog", "udplog", "filelog", "httplog", "request_transformer"})

assert.has_error(function()
spec_helper.start_kong(SERVER_CONF, true)
end, "You are using a plugin that has not been enabled in the configuration: ratelimiting")
end)

it("should work the used plugins are enabled", function()
replace_conf_property("plugins_available", {"ssl", "keyauth", "basicauth", "tcplog", "udplog", "filelog", "request_transformer", "ratelimiting", "cors"})
replace_conf_property("plugins_available", {"ssl", "keyauth", "basicauth", "tcplog", "udplog", "filelog", "httplog", "request_transformer", "ratelimiting", "cors"})

local _, exit_code = spec_helper.start_kong(SERVER_CONF, true)
assert.are.same(0, exit_code)
Expand Down
20 changes: 20 additions & 0 deletions spec/plugins/logging_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ local TEST_CONF = "kong_TEST.yml"

local TCP_PORT = 7777
local UDP_PORT = 8888
local HTTP_PORT = 8989

describe("Logging Plugins", function()

Expand All @@ -26,6 +27,7 @@ describe("Logging Plugins", function()
plugin_configuration = {
{ name = "tcplog", value = { host = "127.0.0.1", port = 7777 }, __api = 1 },
{ name = "udplog", value = { host = "127.0.0.1", port = 8888 }, __api = 1 },
{ name = "httplog", value = { http_endpoint = "http://localhost:"..HTTP_PORT, method = "POST"}, __api = 1 },
{ name = "filelog", value = {}, __api = 1 }
}
}
Expand Down Expand Up @@ -73,6 +75,24 @@ describe("Logging Plugins", function()
assert.are.same("127.0.0.1", log_message.ip)
end)

it("should log to Http", function()
local thread = spec_helper.start_http_server(HTTP_PORT) -- Starting the mock TCP server

-- Making the request
local _, status = http_client.get(STUB_GET_URL, nil, { host = "logging.com" })
assert.are.equal(200, status)

-- Getting back the TCP server input
local ok, res = thread:join()
assert.truthy(ok)
assert.truthy(res)

-- Making sure it's alright
assert.are.same("POST / HTTP/1.1", res[1])
local log_message = cjson.decode(res[7])
assert.are.same("127.0.0.1", log_message.ip)
end)

it("should log to file", function()
local uuid = string.gsub(uuid(), "-", "")

Expand Down
34 changes: 34 additions & 0 deletions spec/spec_helpers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,40 @@ function _M.start_tcp_server(port, ...)
return thread
end


-- Starts a HTTP server
-- @param `port` The port where the server will be listening to
-- @return `thread` A thread object
function _M.start_http_server(port, ...)
local thread = Threads.new({
function(port)
local socket = require "socket"
local server = assert(socket.bind("*", port))
local client = server:accept()
local lines = {}
local count = 1
local line, err = nil, nil
while true do
line, err = client:receive()
if not err then
lines[count] = line
line = nil
if count == 7 then
client:send("ok" .. "\n")
break
end
count = count + 1;
end
end
client:close()
return lines
end;
}, port)

thread:start(...)
return thread
end

-- Starts a UDP server
-- @param `port` The port where the server will be listening to
-- @return `thread` A thread object
Expand Down
1 change: 1 addition & 0 deletions spec/unit/statics_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ plugins_available:
- tcplog
- udplog
- filelog
- httplog
- cors
- request_transformer

Expand Down