Skip to content

Commit 19d3913

Browse files
authored
Embrace Crystal standard Log for logging. (#705)
* Embrace Crystal standard Log for logging. Kemal uses a LogHandler to log requests, this code predates the Crystal Log class so while the Kemal documentation says that logging is done using Log class, the http request log is done in a different way. This patch deprecates: - Kemal::Config#logger - Kemal::Config#logger=(Kemal::BaseLogHandler) - log(String) - Kemal::LogHandler.initialize(IO) - NullLogHandler and changes: - Add Kemal::Log (Log = ::Log.for(self)) - Kemal::LogHandler now uses Log. - No handler is created if logging is set to false. Old code using custom log handlers must work as before. * Let ExceptionHandler use Log instead of log. * Deprecate Kemal::LogHandler and adds Kemal::RequestLogHandler. * Don't break API on Kemal#logger. * Add test for Kemal::RequestLogHandler. * Do not log redundant informations like timestamp and exception message. * Use ex.message on unexpected exceptions log message.
1 parent 4352774 commit 19d3913

11 files changed

+70
-22
lines changed

spec/config_spec.cr

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe "Config" do
2929
config = Kemal.config
3030
config.add_handler CustomTestHandler.new
3131
Kemal.config.setup
32-
config.handlers.size.should eq(8)
32+
config.handlers.size.should eq(7)
3333
end
3434

3535
it "toggles the shutdown message" do

spec/helpers_spec.cr

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe "Macros" do
1313
it "adds a custom handler" do
1414
add_handler CustomTestHandler.new
1515
Kemal.config.setup
16-
Kemal.config.handlers.size.should eq 8
16+
Kemal.config.handlers.size.should eq 7
1717
end
1818
end
1919

@@ -151,7 +151,7 @@ describe "Macros" do
151151
it "adds HTTP::CompressHandler to handlers" do
152152
gzip true
153153
Kemal.config.setup
154-
Kemal.config.handlers[5].should be_a(HTTP::CompressHandler)
154+
Kemal.config.handlers[4].should be_a(HTTP::CompressHandler)
155155
end
156156
end
157157

spec/log_handler_spec.cr

-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
require "./spec_helper"
22

33
describe "Kemal::LogHandler" do
4-
it "logs to the given IO" do
5-
io = IO::Memory.new
6-
logger = Kemal::LogHandler.new io
7-
logger.write "Something"
8-
io.to_s.should eq "Something"
9-
end
10-
114
it "creates log message for each request" do
125
request = HTTP::Request.new("GET", "/")
136
io = IO::Memory.new

spec/request_log_handler_spec.cr

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
require "log/spec"
2+
require "./spec_helper"
3+
4+
describe Kemal::RequestLogHandler do
5+
it "creates log message for each request" do
6+
Log.setup(:none)
7+
8+
request = HTTP::Request.new("GET", "/")
9+
response = HTTP::Server::Response.new(IO::Memory.new)
10+
context = HTTP::Server::Context.new(request, response)
11+
logger = Kemal::RequestLogHandler.new
12+
Log.capture do |logs|
13+
logger.call(context)
14+
logs.check(:info, /404 GET \/ \d+.*s/)
15+
end
16+
end
17+
end

src/kemal.cr

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
require "http"
22
require "json"
3+
require "log"
34
require "uri"
45
require "./kemal/*"
56
require "./kemal/ext/*"
67
require "./kemal/helpers/*"
78

89
module Kemal
10+
Log = ::Log.for(self)
11+
912
# Overload of `self.run` with the default startup logging.
1013
def self.run(port : Int32?, args = ARGV, trap_signal : Bool = true)
1114
self.run(port, args, trap_signal) { }
@@ -68,9 +71,9 @@ module Kemal
6871
def self.display_startup_message(config, server)
6972
if config.env != "test"
7073
addresses = server.addresses.join ", " { |address| "#{config.scheme}://#{address}" }
71-
log "[#{config.env}] #{config.app_name} is ready to lead at #{addresses}"
74+
Log.info { "[#{config.env}] #{config.app_name} is ready to lead at #{addresses}" }
7275
else
73-
log "[#{config.env}] #{config.app_name} is running in test mode. Server not listening"
76+
Log.info { "[#{config.env}] #{config.app_name} is running in test mode. Server not listening" }
7477
end
7578
end
7679

@@ -94,7 +97,7 @@ module Kemal
9497

9598
private def self.setup_trap_signal
9699
Process.on_terminate do
97-
log "#{Kemal.config.app_name} is going to take a rest!" if Kemal.config.shutdown_message
100+
Log.info { "#{Kemal.config.app_name} is going to take a rest!" } if Kemal.config.shutdown_message
98101
Kemal.stop
99102
exit
100103
end

src/kemal/config.cr

+13-7
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,17 @@ module Kemal
4545
@handler_position = 0
4646
end
4747

48+
@[Deprecated("Use standard library Log")]
4849
def logger
49-
@logger.not_nil!
50+
@logger || NullLogHandler.new
5051
end
5152

53+
# :nodoc:
54+
def logger?
55+
@logger
56+
end
57+
58+
@[Deprecated("Use standard library Log")]
5259
def logger=(logger : Kemal::BaseLogHandler)
5360
@logger = logger
5461
end
@@ -134,12 +141,11 @@ module Kemal
134141
end
135142

136143
private def setup_log_handler
137-
@logger ||= if @logging
138-
Kemal::LogHandler.new
139-
else
140-
Kemal::NullLogHandler.new
141-
end
142-
HANDLERS.insert(@handler_position, @logger.not_nil!)
144+
return unless @logging
145+
146+
log_handler = @logger || Kemal::RequestLogHandler.new
147+
148+
HANDLERS.insert(@handler_position, log_handler)
143149
@handler_position += 1
144150
end
145151

src/kemal/exception_handler.cr

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ module Kemal
2121
end
2222
end
2323

24-
log("Exception: #{ex.inspect_with_backtrace}")
24+
Log.error(exception: ex) { ex.message }
2525
# Else use generic 500 handler if defined
2626
return call_exception_with_status_code(context, ex, 500) if Kemal.config.error_handlers.has_key?(500)
2727
verbosity = Kemal.config.env == "production" ? false : true

src/kemal/helpers/helpers.cr

+8-1
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@ end
3232

3333
# Logs the output via `logger`.
3434
# This is the built-in `Kemal::LogHandler` by default which uses STDOUT.
35+
@[Deprecated("Use standard library Log")]
3536
def log(message : String)
36-
Kemal.config.logger.write "#{message}\n"
37+
logger = Kemal.config.logger?
38+
if logger
39+
logger.write "#{message}\n"
40+
else
41+
Log.info { message }
42+
end
3743
end
3844

3945
# Enables / Disables logging.
@@ -70,6 +76,7 @@ end
7076
# ```
7177
# logger MyCustomLogger.new
7278
# ```
79+
@[Deprecated("Use standard library Log")]
7380
def logger(logger : Kemal::BaseLogHandler)
7481
Kemal.config.logger = logger
7582
end

src/kemal/log_handler.cr

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module Kemal
22
# Uses `STDOUT` by default and handles the logging of request/response process time.
3+
@[Deprecated("Setup Log instead.")]
34
class LogHandler < Kemal::BaseLogHandler
45
def initialize(@io : IO = STDOUT)
56
end

src/kemal/null_log_handler.cr

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module Kemal
22
# This is here to represent the logger corresponding to Null Object Pattern.
3+
@[Deprecated("Use standard library Log")]
34
class NullLogHandler < Kemal::BaseLogHandler
45
def call(context : HTTP::Server::Context)
56
call_next(context)

src/kemal/request_log_handler.cr

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module Kemal
2+
# :nodoc:
3+
class RequestLogHandler
4+
include HTTP::Handler
5+
6+
def call(context : HTTP::Server::Context)
7+
elapsed_time = Time.measure { call_next(context) }
8+
elapsed_text = elapsed_text(elapsed_time)
9+
Log.info { "#{context.response.status_code} #{context.request.method} #{context.request.resource} #{elapsed_text}" }
10+
context
11+
end
12+
13+
private def elapsed_text(elapsed)
14+
millis = elapsed.total_milliseconds
15+
return "#{millis.round(2)}ms" if millis >= 1
16+
17+
"#{(millis * 1000).round(2)}µs"
18+
end
19+
end
20+
end

0 commit comments

Comments
 (0)