Skip to content

Commit

Permalink
Logger#formatter=(fmt : String)
Browse files Browse the repository at this point in the history
  • Loading branch information
maiha committed Jan 24, 2019
1 parent 8a2602a commit 08ef538
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 0 deletions.
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ logger.info("hello")
# => (a file) "hello"
```

In addition, this add a `Logger#formatter=(str : String)` method to core library.
See: [src/ext/logger.cr](./src/ext/logger.cr)

## Installation

Add this to your application's `shard.yml`:
Expand Down Expand Up @@ -51,6 +54,38 @@ unless logger.memory.to_s.empty?
end
```

### `Logger#formatter=(fmt : String)`

This library enhanced stdlib `Logger#formtter=` to accept format string.

The traditional way to set formatter is not pretty.
```crystal
logger.formatter = Logger::Formatter.new do |severity, datetime, progname, message, io|
io << severity.to_s << "," << message
end
logger.info "foo" # => "INFO,foo\n"
```

This can be simply refactored by `{{KEYWORD(=FORMAT)}}` as follows.
```crystal
logger.formatter = "{{severity}},{{message}}"
logger.info "foo" # => "INFO,foo\n"
```

##### available keywords

|Keyword |Alias | Converted to | Example |
|------------------|----------|-----------------------|-----------------------|
|`{{level}}` |`severity`|`severity` | "INFO" |
|`{{level=[%-5s]}}`| |`severity` | "[INFO ]" |
|`{{mark}}` | |`severity.to_s[0]` | "I" |
|`{{time}}` |`datetime`|`datetime` | "2019-01-24 21:03:45" |
|`{{time=%H:%M}}` | |`datetime.to_s("...")` | "21:03" |
|`{{name}}` |`progname`|`progname` | "main" |
|`{{message}}` | |`message` | "foo" |
|`{{pid=%6s}}` | |`Process.pid` | " 5361" |
|`{{xxx}}` | | (leaves unknowns) | "{{xxx}}" |

## Contributing

1. Fork it (<https://github.com/maiha/composite_logger.cr/fork>)
Expand Down
74 changes: 74 additions & 0 deletions spec/logger_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
require "./spec_helper"

describe Logger do
describe "#formatter=" do
it "ensures backward compats" do
io = IO::Memory.new
logger = Logger.new(io)
logger.formatter = Logger::Formatter.new do |severity, datetime, progname, message, io|
io << severity.to_s << "," << message
end
logger.info "foo"
io.to_s.should eq("INFO,foo\n")
end

it "also accepts String for handy settings" do
io = IO::Memory.new
logger = Logger.new(io)
logger.formatter = "{{severity}},{{message}}"
logger.info "foo"
io.to_s.should eq("INFO,foo\n")
end

it "accepts '{{mark}}'" do
apply("{{mark}}").should eq("I")
end

it "accepts '{{severity}}' and '{{level}}'" do
apply("{{severity}}").should eq("INFO")
apply("{{level}}").should eq("INFO")
end

it "accepts '{{datetime=FORMAT}}' and '{{time}}'" do
apply("{{datetime=%Y}}").should eq(Time.now.to_s("%Y"))
apply("{{time=%Y}}").should eq(Time.now.to_s("%Y"))
end

it "accepts '{{progname}}' and '{{prog}}'" do
apply("{{progname}}").should eq("main")
apply("{{prog}}").should eq("main")
end

it "accepts '{{message}}'" do
apply("{{message}}").should eq("foo")
end

it "accepts '{{pid}}'" do
apply("{{pid}}").should match(/^\d+$/)
end

it "accepts '{{KEYWORD=FORMAT}}'" do
apply("{{severity}}").should eq("INFO")
apply("{{severity=%5s}}").should eq(" INFO")
apply("{{severity=%-5s}}").should eq("INFO ")
end

it "accepts '{{KEYWORD=FORMAT}}' and call format only when not empty" do
apply("{{prog}}", "foo").should eq("foo")
apply("{{prog=[%s]}}", "foo").should eq("[foo]")
apply("{{prog=[%s]}}", "").should eq("")
end

it "leaves them alone for unknown keywords" do
apply("{{xxx}}").should eq("{{xxx}}")
end
end
end

private def apply(fmt : String, prog = "main") : String
io = IO::Memory.new
logger = Logger.new(io)
logger.formatter = fmt
logger.info "foo", prog
return io.to_s.chomp
end
15 changes: 15 additions & 0 deletions src/composite_logger.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "logger"
require "./ext/logger"

class CompositeLogger < Logger
include Enumerable(Logger)
Expand Down Expand Up @@ -67,4 +68,18 @@ class CompositeLogger
def self.new(logger : Logger, **args) : CompositeLogger
CompositeLogger.new([logger], **args)
end

def self.new(loggers : Array(Hash(String, String)), **args) : CompositeLogger
loggers = loggers.map{|hash|
mode = hash["mode"]?.try(&.to_s) || "w+"
path = hash["path"]?.try(&.to_s) || "STDOUT"
io = {"STDOUT" => STDOUT, "STDERR" => STDERR}[path]? || File.open(path, mode)
logger = Logger.new(io)
logger.level = Logger::Severity.parse(hash["level"].to_s) if hash["level"]?
logger.formatter = hash["format"].to_s if hash["format"]?
logger
}

CompositeLogger.new(loggers, **args)
end
end
38 changes: 38 additions & 0 deletions src/ext/logger.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Add feature about `formatter=(str : String)` to `Logger`
#
# ```crystal
# logger.formatter = "{{mark}}, [{{time=%H:%M}}] {{prog=[%s] }}{{message}}"
# logger.info "foo", "main" # => "I, 23:57 [main] foo"
# ```
class Logger
def formatter=(str : String)
@formatter = Logger::Formatter.new do |severity, datetime, progname, message, io|
io << str.gsub(/\{\{([^=}]+)=?(.*?)\}\}/) {
key = $1
fmt = $2.empty? ? nil : $2
begin
v = case key
when "mark" ; severity.to_s[0]
when "severity", "level" ; severity
when "datetime", "time" ; datetime
when "progname", "prog" ; progname
when "message" ; message
when "pid" ; Process.pid
else ; "{{#{key}}}"
end
if v.is_a?(Time)
fmt ? v.to_s(fmt) : v.to_s
else
if fmt && !v.to_s.empty?
fmt % v
else
v.to_s
end
end
rescue err
"{{#{key}:#{err}}}"
end
}
end
end
end

0 comments on commit 08ef538

Please sign in to comment.