mr_loga_loga 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/BUG_REPORT.md +27 -0
- data/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +24 -0
- data/.github/ISSUE_TEMPLATE/config.yml +1 -0
- data/.github/dependabot.yml +17 -0
- data/.github/stale.yml +9 -0
- data/.github/workflows/main.yml +58 -0
- data/.gitignore +190 -0
- data/.rspec +3 -0
- data/.rubocop.yml +32 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +67 -0
- data/LICENSE.txt +21 -0
- data/README.md +196 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/mr_loga_loga/configuration.rb +27 -0
- data/lib/mr_loga_loga/context.rb +80 -0
- data/lib/mr_loga_loga/extensions/lograge_patch.rb +43 -0
- data/lib/mr_loga_loga/formatters/json.rb +42 -0
- data/lib/mr_loga_loga/formatters/key_value.rb +36 -0
- data/lib/mr_loga_loga/instance_methods.rb +26 -0
- data/lib/mr_loga_loga/logger.rb +83 -0
- data/lib/mr_loga_loga/logger_proxy.rb +39 -0
- data/lib/mr_loga_loga/version.rb +5 -0
- data/lib/mr_loga_loga.rb +39 -0
- data/logo.png +0 -0
- data/mr_loga_loga.gemspec +40 -0
- metadata +133 -0
data/README.md
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
<div align="center">
|
2
|
+
|
3
|
+
# Mr. Loga Loga
|
4
|
+
|
5
|
+
<img alt="logo" src="logo.png" width="300px" height="auto">
|
6
|
+
|
7
|
+
### The simply bombastic, fantastic logger for Ruby 💎
|
8
|
+
|
9
|
+
[![Gem Version](https://badge.fury.io/rb/mr_loga_loga.svg)](https://badge.fury.io/rb/mr_loga_loga)
|
10
|
+
[![Main](https://github.com/hschne/mr-loga-loga/actions/workflows/main.yml/badge.svg)](https://github.com/hschne/mr-loga-loga/actions/workflows/main.yml)
|
11
|
+
![License](https://img.shields.io/github/license/hschne/mr-loga-loga)
|
12
|
+
|
13
|
+
## What's this?
|
14
|
+
|
15
|
+
MrLogaLoga is a logger for Ruby that allows you to easily attach contextual information to your log messages. When writing logs, messages only tell half the story. MrLogaLoga allows you to make the most of your logs:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
logger.info('message', user: 'name', data: 1)
|
19
|
+
# I, [2022-01-01T12:00:00.000000 #19074] INFO -- Main: message user=user data=1
|
20
|
+
```
|
21
|
+
|
22
|
+
You can find out more about the motivation behind the project [here](#why-mrlogaloga). For usage read [Usage](#usage) or [Advanced Usage](#advanced-usage)
|
23
|
+
|
24
|
+
**Note**: This gem is in early development. Try it out and leave some feedback, it really goes a long way in helping me out with development. Any [feature request](https://github.com/hschne/mr-loga-loga/issues/new?assignees=&labels=type%3ABug&template=FEATURE_REQUEST.md&title=) or [bug report](https://github.com/hschne/mr-loga-loga/issues/new?assignees=&labels=type%3AEnhancement&template=BUG_REPORT.md&title=) is welcome. If you like this project, leave a star to show your support! ⭐
|
25
|
+
|
26
|
+
## Getting Started
|
27
|
+
|
28
|
+
Add this line to your application's Gemfile:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
gem 'mr_loga_loga'
|
32
|
+
```
|
33
|
+
|
34
|
+
And then execute:
|
35
|
+
|
36
|
+
```
|
37
|
+
bundle install
|
38
|
+
```
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
MrLogaLoga provides the same interface as the Ruby default logger you are used to. In addition, however, you can attach contextual information to your log messages:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
require 'mr_loga_loga'
|
46
|
+
|
47
|
+
logger = MrLogaLoga::Logger.new
|
48
|
+
logger.info('message', user: 'name', data: 1)
|
49
|
+
# I, [2022-01-01T12:00:00.000000 #19074] INFO -- Main: message user=user data=1
|
50
|
+
logger.context(class: 'classname').warn('message')
|
51
|
+
# W, [2022-01-01T12:00:00.000000 #19074] WARN -- Main: message class=classname
|
52
|
+
```
|
53
|
+
|
54
|
+
To customize how log messages are formatted see [Formatters][#formatters].
|
55
|
+
|
56
|
+
## Advanced Usage
|
57
|
+
|
58
|
+
MrLogaLoga provides a fluent interface to build log messages. For example, to attach an additional `user` field to a log message you can use any of the following:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
logger.info('message', user: 'name')
|
62
|
+
logger.context(user: 'name').info('message') # Explicit context
|
63
|
+
logger.context { { user: 'name' } }.info('message') # Block context
|
64
|
+
logger.user('name').info('message') # Dynamic context method
|
65
|
+
logger.user { 'name' }.info('message') # Dynamic context block
|
66
|
+
```
|
67
|
+
|
68
|
+
The block syntax [ is recommended when logging calculated properties ](https://ruby-doc.org/stdlib-2.4.0/libdoc/logger/rdoc/Logger.html#class-Logger-label-How+to+log+a+message).
|
69
|
+
|
70
|
+
#### Shared Context
|
71
|
+
|
72
|
+
If multiple log messages within the same class should share a context include the `MrLogaLoga` module. Using `logger` will result in the defined context being included per default:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
class MyClass
|
76
|
+
include MrLogaLoga
|
77
|
+
|
78
|
+
# This is the default. You may overwrite this in your own classes
|
79
|
+
def loga_context
|
80
|
+
{ class_name: self.class.name }
|
81
|
+
end
|
82
|
+
|
83
|
+
def log
|
84
|
+
# This includes the class name in the log message now
|
85
|
+
logger.debug('debug') # debug class_name=MyClass
|
86
|
+
# Additional context will be merged
|
87
|
+
logger.debug('debug', user: 'user') # debug class_name=MyClass user=user
|
88
|
+
end
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
When used with [Rails](#rails) logger will default to `Rails.logger`. If you use MrLogaLoga outside of Rails, you can either configure the logger instance to be used globally, or by overwriting the `loga_loga` method:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
# In some configuration class
|
96
|
+
MrLogaLoga.configure do |configuration|
|
97
|
+
logger = MrLogaLoga::Logger.new($stdout)
|
98
|
+
end
|
99
|
+
|
100
|
+
# In the class where you do the logging itself
|
101
|
+
class MyClass
|
102
|
+
include MrLogaLoga
|
103
|
+
|
104
|
+
def loga_loga
|
105
|
+
MrLogaLoga::Logger.new($stdout)
|
106
|
+
end
|
107
|
+
|
108
|
+
def log
|
109
|
+
# ...
|
110
|
+
end
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
|
115
|
+
### Formatters
|
116
|
+
|
117
|
+
MrLogaLoga uses the [KeyValue](https://github.com/hschne/mr-loga-loga/blob/main/lib/mr_loga_loga/formatters/key_value.rb) formatter per default. The [Json](https://github.com/hschne/mr-loga-loga/blob/main/lib/mr_loga_loga/formatters/json.rb) formatter is also included. To use a specific formatter pass it to the logger constructor:
|
118
|
+
|
119
|
+
```Ruby
|
120
|
+
|
121
|
+
MrLogaLoga::Logger.new(STDOUT, formatter: MrLogaLoga::Formatters::KeyValue.new)
|
122
|
+
```
|
123
|
+
|
124
|
+
You can implement and add your own formatters like so:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
class MyFormatter
|
128
|
+
def call(severity, datetime, progname, message, context)
|
129
|
+
context = context.map { |key, value| "#{key}=#{value}" }.compact.join(' ')
|
130
|
+
"#{severity} #{datetime.strftime('%Y-%m-%dT%H:%M:%S.%6N')} #{progname} #{message} #{context}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
MrLogaLoga::Logger.new(STDOUT, formatter: MyFormatter.new)
|
135
|
+
```
|
136
|
+
|
137
|
+
### Rails
|
138
|
+
|
139
|
+
Using MrLogaLoga in Ruby on Rails is straightforward. Set up MrLogaLoga as logger in your `application.rb` or environment files and you are off to the races:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
# application.rb
|
143
|
+
config.logger = MrLogaLoga::Logger.new(STDOUT)
|
144
|
+
config.log_level = :info
|
145
|
+
```
|
146
|
+
|
147
|
+
Note that setting `config.log_formatter` does not work. You must set the formatter in the logger constructor as described in [Formatters](#formatters).
|
148
|
+
|
149
|
+
### Lograge
|
150
|
+
|
151
|
+
[LogRage](https://github.com/roidrage/lograge) and MrLogaLoga work well together. When using both gems Lograge will be patched so that data will be available as `context` in MrLogaLoga. Make sure that MrLogaLoga is required **after** Lograge:
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
gem 'lograge'
|
155
|
+
gem 'mr_loga_loga'
|
156
|
+
```
|
157
|
+
|
158
|
+
Note that Lograge's formatters won't be used. Use MrLogaLoga's own [formatters](#formatters) instead.
|
159
|
+
|
160
|
+
## Why MrLogaLoga?
|
161
|
+
|
162
|
+
The more context your logs provide, the more use you will get out of them. The standard Ruby logger only takes a string as an argument, so you have to resort to something like this:
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
logger.debug("my message user=#{user} more_data=#{data}")
|
166
|
+
```
|
167
|
+
|
168
|
+
This is fine, as long as you do not need to change your log format. Changing your log formatter will not change the format of your message, nor the formatting of the contextual information you provided.
|
169
|
+
|
170
|
+
MrLogaLoga addresses this by allowing you to attach contextual information to your logs and giving you full control over how both message and context are formatted.
|
171
|
+
|
172
|
+
## Credit
|
173
|
+
|
174
|
+
This little library was inspired by [Lograge](https://github.com/roidrage/lograge) first and foremost. I would like to thank the amazing [@LenaSchnedlitz](https://twitter.com/LenaSchnedlitz) for the incredible logo! 🤩
|
175
|
+
|
176
|
+
## Development
|
177
|
+
|
178
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests and linter. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
179
|
+
|
180
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
181
|
+
|
182
|
+
## Contributing
|
183
|
+
|
184
|
+
Thank you for contributing! :heart:
|
185
|
+
|
186
|
+
We welcome all support, whether on bug reports, code, design, reviews, tests, documentation, translations, or just feature requests.
|
187
|
+
|
188
|
+
Please use [GitHub issues](https://github.com/hschne/rails-mini-profiler/issues) to submit bugs or feature requests.
|
189
|
+
|
190
|
+
## License
|
191
|
+
|
192
|
+
The gem is available as open-source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
193
|
+
|
194
|
+
## Code of Conduct
|
195
|
+
|
196
|
+
Everyone interacting in the MrLogaLoga project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [code of conduct](https://github.com/hschne/mr_loga_loga/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'mr_loga_loga'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MrLogaLoga
|
4
|
+
# == Description
|
5
|
+
#
|
6
|
+
# The configuration class for MrLogaLoga
|
7
|
+
#
|
8
|
+
# == Usage
|
9
|
+
#
|
10
|
+
# MrLogaLoga.configure do |configuration|
|
11
|
+
# configuration.logger = ...
|
12
|
+
# end
|
13
|
+
class Configuration
|
14
|
+
attr_accessor :logger
|
15
|
+
|
16
|
+
# Initialize the configuration by setting configuration default values
|
17
|
+
def initialize(**kwargs)
|
18
|
+
reset
|
19
|
+
kwargs.each { |key, value| instance_variable_set("@#{key}", value) }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Reset the configuration to default values
|
23
|
+
def reset
|
24
|
+
@logger = MrLogaLoga::Logger.new($stdout)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module MrLogaLoga
|
6
|
+
# == Description
|
7
|
+
#
|
8
|
+
# This class provides a fluent interface to attach contextual information to log messages.
|
9
|
+
class Context
|
10
|
+
def initialize(logger, context = {})
|
11
|
+
@logger = logger
|
12
|
+
@context = context
|
13
|
+
end
|
14
|
+
|
15
|
+
def context(context = {}, &block)
|
16
|
+
@context = merge_context(@context, context)
|
17
|
+
@context = merge_context(@context, block)
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def add(severity, message = nil, **context, &block)
|
22
|
+
severity ||= UNKNOWN
|
23
|
+
return true unless @logger.log?(severity)
|
24
|
+
|
25
|
+
context = merge_context(@context, context)
|
26
|
+
context = context.call if context.is_a?(Proc)
|
27
|
+
|
28
|
+
@logger.add(severity, message, **context, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
alias log add
|
32
|
+
|
33
|
+
%i[debug info warn error fatal unknown].each do |symbol|
|
34
|
+
define_method(symbol) do |message = nil, **context, &block|
|
35
|
+
severity = Object.const_get("Logger::Severity::#{symbol.to_s.upcase}")
|
36
|
+
return true unless @logger.log?(severity)
|
37
|
+
|
38
|
+
context = merge_context(@context, context)
|
39
|
+
context = context.call if context.is_a?(Proc)
|
40
|
+
@logger.public_send(symbol, message, **context, &block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def method_missing(symbol, *args, &block)
|
45
|
+
context = block ? -> { { symbol => block.call } } : { symbol => unwrap(args) }
|
46
|
+
@context = merge_context(@context, context)
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def respond_to_missing?(name, include_private = false)
|
51
|
+
super(name, include_private)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def unwrap(args)
|
57
|
+
if args.size == 1
|
58
|
+
args[0]
|
59
|
+
else
|
60
|
+
args
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def merge_context(original, new)
|
65
|
+
return original unless new
|
66
|
+
|
67
|
+
return original.merge(new) if original.is_a?(Hash) && new.is_a?(Hash)
|
68
|
+
|
69
|
+
merge_blocks(original, new)
|
70
|
+
end
|
71
|
+
|
72
|
+
def merge_blocks(original, new)
|
73
|
+
return -> { original.merge(new.call) } if original.is_a?(Hash) && new.is_a?(Proc)
|
74
|
+
|
75
|
+
return -> { original.call.merge(new) } if original.is_a?(Proc) && new.is_a?(Hash)
|
76
|
+
|
77
|
+
-> { original.call.merge(new.call) }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'byebug'
|
4
|
+
|
5
|
+
module MrLogaLoga
|
6
|
+
module Adapters
|
7
|
+
# This patches Lograge to forward data as context to MrLogaLoga
|
8
|
+
module LogragePatch
|
9
|
+
class << self
|
10
|
+
def apply
|
11
|
+
return unless defined?(Lograge)
|
12
|
+
|
13
|
+
patch_applies = defined?(Lograge::LogSubscribers::Base) &&
|
14
|
+
Lograge::LogSubscribers::Base.private_method_defined?(:process_main_event)
|
15
|
+
unless patch_applies
|
16
|
+
puts 'WARNING: Failed to patch Lograge. It looks like '\
|
17
|
+
"MrLogaLoga's patch no longer applies in "\
|
18
|
+
"#{__FILE__}. Please contact MrLogaLoga maintainers."
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
Lograge::LogSubscribers::Base.prepend(self)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def process_main_event(event)
|
27
|
+
return if Lograge.ignore?(event)
|
28
|
+
|
29
|
+
payload = event.payload
|
30
|
+
data = extract_request(event, payload)
|
31
|
+
data = before_format(data, payload)
|
32
|
+
if logger.is_a?(MrLogaLoga::Logger)
|
33
|
+
logger.send(Lograge.log_level, '', **data)
|
34
|
+
else
|
35
|
+
formatted_message = Lograge.formatter.call(data)
|
36
|
+
logger.send(Lograge.log_level, formatted_message)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
MrLogaLoga::Adapters::LogragePatch.apply
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MrLogaLoga
|
4
|
+
module Formatters
|
5
|
+
# == Description
|
6
|
+
#
|
7
|
+
# A simple Json formatter for MrLogaLoga.
|
8
|
+
#
|
9
|
+
# == Format
|
10
|
+
#
|
11
|
+
# The json formatter renders messages into a single-line json. Context keys are embedded on the top level.
|
12
|
+
#
|
13
|
+
# Log Format:
|
14
|
+
#
|
15
|
+
# { "severity": "Severity", .. "message": "Message", "key1": "Key1" }
|
16
|
+
#
|
17
|
+
class Json < Logger::Formatter
|
18
|
+
# Render a log message in JSON
|
19
|
+
#
|
20
|
+
# @param severity [String] The message severity
|
21
|
+
# @param datetime [DateTime] The message date time
|
22
|
+
# @param progname [DateTime] The program name
|
23
|
+
# @param message [String] The log message
|
24
|
+
# @param context [Hash] The log message context
|
25
|
+
#
|
26
|
+
# @return [String] the formatted log message
|
27
|
+
def call(severity, datetime, progname, message, context)
|
28
|
+
message = message.nil? || message.empty? ? nil : msg2str(message)
|
29
|
+
|
30
|
+
message_hash = {
|
31
|
+
severity: severity,
|
32
|
+
datetime: datetime.strftime('%Y-%m-%dT%H:%M:%S.%6N'),
|
33
|
+
pid: Process.pid,
|
34
|
+
progname: progname,
|
35
|
+
message: message,
|
36
|
+
**context
|
37
|
+
}.compact
|
38
|
+
"#{message_hash.to_json}\n"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MrLogaLoga
|
4
|
+
module Formatters
|
5
|
+
# == Description
|
6
|
+
#
|
7
|
+
# A simple key value formatter that extends the standard formatter by rendering additional contextual information.
|
8
|
+
#
|
9
|
+
# == Format
|
10
|
+
#
|
11
|
+
# The key-value formatter renders messages into the following format:
|
12
|
+
#
|
13
|
+
# Log format:
|
14
|
+
#
|
15
|
+
# SeverityID, [DateTime #pid] SeverityLabel -- ProgName: message key1=value1 key2=value2
|
16
|
+
#
|
17
|
+
class KeyValue < Logger::Formatter
|
18
|
+
# Render a log message
|
19
|
+
#
|
20
|
+
# @param severity [String] The message severity
|
21
|
+
# @param datetime [DateTime] The message date time
|
22
|
+
# @param progname [DateTime] The program name
|
23
|
+
# @param message [String] The log message
|
24
|
+
# @param context [Hash] The log message context
|
25
|
+
#
|
26
|
+
# @return [String] the formatted log message
|
27
|
+
def call(severity, datetime, progname, message, context)
|
28
|
+
message = context.map { |key, value| "#{key}=#{value}" }
|
29
|
+
.prepend(message)
|
30
|
+
.compact
|
31
|
+
.join(' ')
|
32
|
+
super(severity, datetime, progname, message)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MrLogaLoga
|
4
|
+
# == Description
|
5
|
+
#
|
6
|
+
# Instance methods to be attached when including the main module.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
module InstanceMethods
|
10
|
+
def loga_context
|
11
|
+
{ class_name: self.class.name }
|
12
|
+
end
|
13
|
+
|
14
|
+
def logger
|
15
|
+
MrLogaLoga::LoggerProxy.new(loga_loga, -> { loga_context })
|
16
|
+
end
|
17
|
+
|
18
|
+
def loga_loga
|
19
|
+
@loga_loga ||= if defined?(Rails.application.logger)
|
20
|
+
Rails.application.logger
|
21
|
+
else
|
22
|
+
MrLogaLoga.configuration.logger
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module MrLogaLoga
|
6
|
+
# == Description
|
7
|
+
#
|
8
|
+
# This class extends the default Ruby Logger to allow users to attach contextual information to log messages.
|
9
|
+
#
|
10
|
+
# === Example
|
11
|
+
#
|
12
|
+
# This creates a Logger that outputs to the standard output stream, with a
|
13
|
+
# level of +WARN+:
|
14
|
+
#
|
15
|
+
# require 'mr_loga_loga'
|
16
|
+
#
|
17
|
+
# logger = MrLogaLoga::Logger.new(STDOUT)
|
18
|
+
# logger.level = Logger::WARN
|
19
|
+
#
|
20
|
+
# logger.debug("Default")
|
21
|
+
# logger.context(user: 1).debug('with context')
|
22
|
+
class Logger < ::Logger
|
23
|
+
def context(**kwargs, &block)
|
24
|
+
context = block ? -> { kwargs.merge(block.call) } : kwargs
|
25
|
+
Context.new(self, context)
|
26
|
+
end
|
27
|
+
|
28
|
+
def message(message, &block)
|
29
|
+
message ||= block
|
30
|
+
Message.new(self, message)
|
31
|
+
end
|
32
|
+
|
33
|
+
def add(severity, message = nil, progname = nil, **context, &block)
|
34
|
+
severity ||= UNKNOWN
|
35
|
+
return true unless log?(severity)
|
36
|
+
|
37
|
+
message = block.call if block
|
38
|
+
@logdev.write(format(format_severity(severity), Time.now, progname, message, context))
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
alias log add
|
43
|
+
|
44
|
+
%i[debug info warn error fatal unknown].each do |symbol|
|
45
|
+
define_method(symbol) do |message = nil, **context, &block|
|
46
|
+
# Map the symbol (e.g. :debug) to the severity constant (e.g. DEBUG)
|
47
|
+
severity = Object.const_get("Logger::Severity::#{symbol.to_s.upcase}")
|
48
|
+
add(severity, message, **context, &block)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_missing(symbol, *args, &block)
|
53
|
+
context = block ? -> { { symbol => block.call } } : { symbol => unwrap(args) }
|
54
|
+
Context.new(self, context)
|
55
|
+
end
|
56
|
+
|
57
|
+
def respond_to_missing?(name, include_private = false)
|
58
|
+
super(name, include_private)
|
59
|
+
end
|
60
|
+
|
61
|
+
def log?(severity)
|
62
|
+
!@logdev.nil? && severity >= level
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def unwrap(args)
|
68
|
+
if args.size == 1
|
69
|
+
args[0]
|
70
|
+
else
|
71
|
+
args
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def format(severity, datetime, progname, message, context)
|
76
|
+
formatter.call(severity, datetime, progname, message, context)
|
77
|
+
end
|
78
|
+
|
79
|
+
def formatter
|
80
|
+
@formatter ||= MrLogaLoga::Formatters::KeyValue.new
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module MrLogaLoga
|
6
|
+
# == Description
|
7
|
+
#
|
8
|
+
# A proxy that attaches contextual information to the underlying logger when called.
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
class LoggerProxy
|
12
|
+
def initialize(logger, context_proc)
|
13
|
+
@logger = logger
|
14
|
+
@context_proc = context_proc
|
15
|
+
end
|
16
|
+
|
17
|
+
def add(severity, message = nil, **context, &block)
|
18
|
+
severity ||= UNKNOWN
|
19
|
+
return true unless @logger.log?(severity)
|
20
|
+
|
21
|
+
context = @context_proc.call.merge(context)
|
22
|
+
|
23
|
+
@logger.add(severity, message, **context, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
alias log add
|
27
|
+
|
28
|
+
%i[debug info warn error fatal unknown].each do |symbol|
|
29
|
+
define_method(symbol) do |message = nil, **context, &block|
|
30
|
+
severity = Object.const_get("Logger::Severity::#{symbol.to_s.upcase}")
|
31
|
+
return true unless @logger.log?(severity)
|
32
|
+
|
33
|
+
context = @context_proc.call.merge(context)
|
34
|
+
|
35
|
+
@logger.public_send(symbol, message, **context, &block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/mr_loga_loga.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'byebug'
|
4
|
+
|
5
|
+
require_relative 'mr_loga_loga/version'
|
6
|
+
require_relative 'mr_loga_loga/configuration'
|
7
|
+
require_relative 'mr_loga_loga/logger_proxy'
|
8
|
+
require_relative 'mr_loga_loga/instance_methods'
|
9
|
+
require_relative 'mr_loga_loga/context'
|
10
|
+
require_relative 'mr_loga_loga/logger'
|
11
|
+
require_relative 'mr_loga_loga/formatters/key_value'
|
12
|
+
require_relative 'mr_loga_loga/formatters/json'
|
13
|
+
|
14
|
+
require_relative 'mr_loga_loga/extensions/lograge_patch'
|
15
|
+
|
16
|
+
# == Description
|
17
|
+
#
|
18
|
+
# The MrLogaLoga module provides additional logging functionality when included in your classes.
|
19
|
+
#
|
20
|
+
module MrLogaLoga
|
21
|
+
class Error < StandardError; end
|
22
|
+
|
23
|
+
def self.included(base)
|
24
|
+
base.send :include, InstanceMethods
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# Create a new configuration object
|
29
|
+
#
|
30
|
+
# @return [Configuration] a new configuration
|
31
|
+
def configuration
|
32
|
+
@configuration ||= Configuration.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def configure
|
36
|
+
yield(configuration)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/logo.png
ADDED
Binary file
|