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

feat(logging): add lualogging compatible logging #149

Merged
merged 3 commits into from
Sep 30, 2024
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
3 changes: 2 additions & 1 deletion example/app.lua
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ local routes do
local name = req.pathParameters.name
if not testData[name] then
local err = ("'%s' is an unknown person"):format(name)
req.log:error(err)
resp:writeDefaultErrorMessage(404, err)
stop = true
end
Expand All @@ -71,7 +72,7 @@ local routes do
-- postFunction runs after the actual method callback
postFunction = function(req, resp)
local stop = false
print("served " .. req.pathParameters.name .. "'s data")
req.log:debug("served %s's data", req.pathParameters.name)
return stop
end,
}
Expand Down
13 changes: 9 additions & 4 deletions example/copas.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ package.path = "./src/?.lua;./src/?/init.lua;"..package.path
-- to make it work.
-- Additionally you need lua-cjson to be installed.

-- require lualogging if available, "pegasus.log" will automatically pick it up
pcall(require, 'logging')

local Handler = require 'pegasus.handler'
local copas = require('copas')
local socket = require('socket')
Expand All @@ -28,7 +31,8 @@ local json = require 'cjson.safe'
-- If not provided, then the connection will be accepted as a plain one.
-- @tparam[opt] table opts.plugins the plugins to use
-- @tparam[opt] function opts.callback the callback function to handle requests
-- @tparam[opt] string opts.location the file-path from where to server files
-- @tparam[opt] string opts.location the file-path from where to serve files
-- @tparam[opt] logger opts.log the LuaLogging logger to use (defaults to LuaLogging default logger)
-- @return the server-socket on success, or nil+err on failure
local function newPegasusServer(opts)
opts = opts or {}
Expand All @@ -48,13 +52,13 @@ local function newPegasusServer(opts)
end
end

local hdlr = Handler:new(opts.callback, opts.location, opts.plugins)
local hdlr = Handler:new(opts.callback, opts.location, opts.plugins, opts.log)

copas.addserver(server_sock, copas.handler(function(client_sock)
hdlr:processRequest(server_port, client_sock)
end, opts.sslparams))

io.stderr:write('Pegasus is up on ' .. (opts.sslparams and "https" or "http") .. "://" .. server_ip .. ":" .. server_port .. "/\n")
hdlr.log:info('Pegasus is up on %s://%s:%s', opts.sslparams and "https" or "http", server_ip, server_port)
return server_sock
end

Expand Down Expand Up @@ -97,6 +101,7 @@ local routes do
local name = req.pathParameters.name
if not testData[name] then
local err = ("'%s' is an unknown person"):format(name)
req.log:error(err)
resp:writeDefaultErrorMessage(404, err)
stop = true
end
Expand All @@ -113,7 +118,7 @@ local routes do
-- postFunction runs after the actual method callback
postFunction = function(req, resp)
local stop = false
print("served " .. req.pathParameters.name .. "'s data")
req.log:debug("served %s's data", req.pathParameters.name)
return stop
end,
}
Expand Down
1 change: 1 addition & 0 deletions rockspecs/pegasus-dev-1.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ build = {
['pegasus.request'] = 'src/pegasus/request.lua',
['pegasus.response'] = 'src/pegasus/response.lua',
['pegasus.compress'] = 'src/pegasus/compress.lua',
['pegasus.log'] = 'src/pegasus/log.lua',
['pegasus.plugins.compress'] = 'src/pegasus/plugins/compress.lua',
['pegasus.plugins.downloads'] = 'src/pegasus/plugins/downloads.lua',
['pegasus.plugins.files'] = 'src/pegasus/plugins/files.lua',
Expand Down
6 changes: 5 additions & 1 deletion spec/unit/request_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ local function getInstance(headers)
end
}

return Request:new(8080, client, server)
local handler = {
log = require "pegasus.log"
}

return Request:new(8080, client, server, handler)
end

local function verifyHttpMethod(method)
Expand Down
21 changes: 11 additions & 10 deletions spec/unit/response_spec.lua
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
local Response = require 'pegasus.response'
local Handler = require 'pegasus.handler'
Handler.log = require 'pegasus.log'

describe('response', function()
describe('instance', function()
local function verifyMethod(method)
local response = Response:new({close=function () end})
local response = Response:new({close=function () end}, Handler)
assert.equal(type(response[method]), 'function')
end

it('should exists constructor to response class', function()
local response = Response:new({close=function () end})
local response = Response:new({close=function () end}, Handler)
assert.equal(type(response), 'table')
end)

Expand Down Expand Up @@ -75,14 +76,14 @@ describe('response', function()

describe('add header', function()
it('should add correct header passed as a parameter', function()
local response = Response:new({})
local response = Response:new({}, Handler)
response:addHeader('Content-Length', 100)

assert.equal(response._headers['Content-Length'], 100)
end)

it('should do a merge with headers already passed', function()
local response = Response:new({})
local response = Response:new({}, Handler)
response:addHeader('Content-Length', 100)
response:addHeader('Content-Type', 'text/html')
response:addHeaders({
Expand All @@ -98,7 +99,7 @@ describe('response', function()
end)

it("should allow multi-value/duplicate headers", function()
local response = Response:new({})
local response = Response:new({}, Handler)
response:addHeader('My-Header', { 'value 1', 'value 2' })
local expected = "My-Header: value 1" .. "\r\n"
.. "My-Header: value 2" .. "\r\n"
Expand All @@ -108,7 +109,7 @@ describe('response', function()
it("fails if headers were already sent", function()
local response = Response:new({
send = function() end,
})
}, Handler)
response:sendHeaders()
assert.has.error(function()
response:addHeader("Hi", "there")
Expand All @@ -118,7 +119,7 @@ describe('response', function()

describe('status code', function()
local verifyStatus = function(statusCode, statusText, expectedMessage)
local response = Response:new({})
local response = Response:new({}, Handler)
response:statusCode(statusCode, statusText)
local expectedStatus = 'HTTP/1.1 ' .. tostring(statusCode)

Expand All @@ -143,7 +144,7 @@ describe('response', function()
end)

it('fails for an unknown status code', function()
local response = Response:new({})
local response = Response:new({}, Handler)
assert.has.error(function()
response:statusCode(9999)
end, "http status code '9999' is unknown")
Expand All @@ -152,7 +153,7 @@ describe('response', function()
it("fails if headers were already sent", function()
local response = Response:new({
send = function() end,
})
}, Handler)
response:sendHeaders()
assert.has.error(function()
response:statusCode(200)
Expand All @@ -178,7 +179,7 @@ describe('response', function()
end)

it('should keep value previously set', function()
local response = Response:new(client)
local response = Response:new(client, Handler)
response:addHeader('Content-Type', 'application/javascript')
response:addHeader('Content-Length', 100)

Expand Down
8 changes: 6 additions & 2 deletions src/pegasus/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ local Files = require 'pegasus.plugins.files'
local Handler = {}
Handler.__index = Handler

function Handler:new(callback, location, plugins)
function Handler:new(callback, location, plugins, logger)
local handler = {}
handler.log = logger or require('pegasus.log')
handler.callback = callback
handler.plugins = plugins or {}

Expand All @@ -15,6 +16,9 @@ function Handler:new(callback, location, plugins)
location = location,
default = "/index.html",
}
handler.log:debug('Handler created, location: %s', location)
else
handler.log:debug('Handler created, without location')
end

local result = setmetatable(handler, self)
Expand All @@ -37,7 +41,7 @@ end
function Handler:pluginsNewConnection(client)
for _, plugin in ipairs(self.plugins) do
if plugin.newConnection then
client = plugin:newConnection(client)
client = plugin:newConnection(client, self)
if not client then
return false
end
Expand Down
12 changes: 9 additions & 3 deletions src/pegasus/init.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
local socket = require 'socket'
local Handler = require 'pegasus.handler'

-- require lualogging if available, "pegasus.log" will automatically pick it up
pcall(require, 'logging')

local Pegasus = {}
Pegasus.__index = Pegasus

Expand All @@ -13,15 +16,18 @@ function Pegasus:new(params)
server.location = params.location or ''
server.plugins = params.plugins or {}
server.timeout = params.timeout or 1
server.log = params.log or require('pegasus.log')

return setmetatable(server, self)
end

function Pegasus:start(callback)
local handler = Handler:new(callback, self.location, self.plugins)
local handler = Handler:new(callback, self.location, self.plugins, self.log)
local server = assert(socket.bind(self.host, self.port))
local ip, port = server:getsockname()
print('Pegasus is up on ' .. ip .. ":".. port)

print('Pegasus is up on ' .. ip .. ":".. port) -- needed in case no LuaLogging is available
handler.log:info('Pegasus is up on %s:%s', ip, port)

while 1 do
local client, errmsg = server:accept()
Expand All @@ -30,7 +36,7 @@ function Pegasus:start(callback)
client:settimeout(self.timeout, 'b')
handler:processRequest(self.port, client, server)
else
io.stderr:write('Failed to accept connection:' .. errmsg .. '\n')
handler.log:error('Failed to accept connection: %s', errmsg)
end
end
end
Expand Down
18 changes: 18 additions & 0 deletions src/pegasus/log.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- logging

-- returns a LuaLogging compatible logger object.
-- If LuaLogging was already loaded, it returns the defaultlogger,
-- otherwise returns a stub. The stub has only no-op functions.

local ll = package.loaded.logging
if ll and type(ll) == "table" and ll.defaultLogger and
tostring(ll._VERSION):find("LuaLogging") then
-- default LuaLogging logger is available
return ll.defaultLogger()
else
-- just use a stub logger with only no-op functions
local nop = function() end
return setmetatable({}, {
__index = function(self, key) self[key] = nop return nop end
})
end
7 changes: 4 additions & 3 deletions src/pegasus/plugins/tls.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function TLS:new(sslparams)
}, TLS)
end

function TLS:newConnection(client)
function TLS:newConnection(client, handler)
local params = self.sslparams

-- wrap the client socket and replace it
Expand All @@ -42,8 +42,9 @@ function TLS:newConnection(client)
assert(client:sni(params.sni.names, params.sni.strict))
end

if not client:dohandshake() then
print"tls handshake failed"
local ok, err = client:dohandshake()
if not ok then
handler.log:error("tls handshake failed: %s", err)
return false
end

Expand Down
3 changes: 3 additions & 0 deletions src/pegasus/request.lua
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ function Request:new(port, client, server, handler)
local obj = {}
obj.client = client
obj.server = server
obj.log = handler.log
obj.port = port
obj.ip = (client.getpeername or function() end)(client) -- luasec doesn't support this method
obj.querystring = {}
Expand Down Expand Up @@ -81,6 +82,8 @@ function Request:parseFirstLine()
end
self.response:skipBody(method == "HEAD")

self.log:info('Request for: %s %s', method, path)

local filename = ''
local querystring = ''

Expand Down
1 change: 1 addition & 0 deletions src/pegasus/response.lua
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Response.__index = Response

function Response:new(client, writeHandler)
local newObj = {}
newObj.log = writeHandler.log
newObj._headersSended = false
newObj._templateFirstLine = 'HTTP/1.1 {{ STATUS_CODE }} {{ STATUS_TEXT }}\r\n'
newObj._headFirstLine = ''
Expand Down
Loading