gallus 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/.editorconfig +12 -0
- data/.gitignore +9 -0
- data/CHANGELOG.md +16 -0
- data/Dockerfile +11 -0
- data/Gemfile +4 -0
- data/README.md +227 -0
- data/Rakefile +27 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/docker-compose.yml +4 -0
- data/examples/current_thread_context.rb +28 -0
- data/examples/custom_format.rb +10 -0
- data/examples/custom_output.rb +9 -0
- data/examples/custom_serialization.rb +10 -0
- data/examples/global_context.rb +19 -0
- data/examples/json_serialization.rb +9 -0
- data/examples/lambda_context_vars.rb +9 -0
- data/examples/simple_console.rb +13 -0
- data/examples/simple_log.rb +12 -0
- data/gallus.gemspec +31 -0
- data/hacking/benchmarks.rb +47 -0
- data/lib/gallus/event.rb +15 -0
- data/lib/gallus/format/simple_console.rb +25 -0
- data/lib/gallus/format/simple_log.rb +18 -0
- data/lib/gallus/level.rb +50 -0
- data/lib/gallus/log.rb +162 -0
- data/lib/gallus/logging.rb +35 -0
- data/lib/gallus/output/file.rb +15 -0
- data/lib/gallus/output/null.rb +9 -0
- data/lib/gallus/output/stderr.rb +10 -0
- data/lib/gallus/output/stdout.rb +10 -0
- data/lib/gallus/output/stream.rb +19 -0
- data/lib/gallus/package_logging.rb +19 -0
- data/lib/gallus/payload.rb +26 -0
- data/lib/gallus/repository.rb +53 -0
- data/lib/gallus/serialization/inspect.rb +11 -0
- data/lib/gallus/serialization/json.rb +13 -0
- data/lib/gallus/version.rb +3 -0
- data/lib/gallus.rb +22 -0
- metadata +195 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 411a743f66a68e2f25e37c20ff108513e9b4baf6
|
4
|
+
data.tar.gz: ea6360329a24846efa92d86cf3a1250afdb116cd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 14e6d3d70973ca0fcc6d194782100c16dc926e89458d61d200c0ad577f96082f876c38a5fd213e9f64e164ffe09b2ba717ea96c43b01b12c44f4488de82efeb0
|
7
|
+
data.tar.gz: e81bae8f87270af9a4297c0237b4017463ecc8a580cf4ed8ebadbfa74cc348f7e3337bdf76546163ecfcc2e30dfea7814b68ee11c33b7d4609a12e41658efb43
|
data/.editorconfig
ADDED
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Change Log
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
This project adheres to [Semantic Versioning](http://semver.org/).
|
4
|
+
|
5
|
+
## [Unreleased]
|
6
|
+
|
7
|
+
...
|
8
|
+
|
9
|
+
## [0.1.0]
|
10
|
+
|
11
|
+
...
|
12
|
+
|
13
|
+
### Added
|
14
|
+
|
15
|
+
- Project extracted from other library and moved into this standalone gem.
|
16
|
+
- Documentation.
|
data/Dockerfile
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
# Gallus
|
2
|
+
|
3
|
+
[![Codeship Status for jobandtalent/gallus](https://codeship.com/projects/3c59b1b0-9dd1-0133-8d35-5e859e904c15/status?branch=master)](https://codeship.com/projects/127615)
|
4
|
+
|
5
|
+
_**Gallus Anonymus**_ (Polonized variant: _**Gall Anonim**_) is the name traditionally given to the anonymous author of Gesta principum Polonorum (Deeds of the Princes of the Poles), composed in Latin about 1115. Gallus is generally regarded as the first historian to have described Poland. His Chronicles are an obligatory text for university courses in Poland's history.
|
6
|
+
|
7
|
+
![Gallus Anonymus](http://i.imgur.com/QpQ2g7B.jpg)
|
8
|
+
|
9
|
+
Picture stolen from: https://www.flickr.com/photos/maxcuo/15601437990
|
10
|
+
|
11
|
+
Gallus is a logger for Ruby apps.
|
12
|
+
|
13
|
+
Q: **Why write yet another logger for ruby?**<br />
|
14
|
+
A: Because culture of logging among ruby developers is very low and tools used are either
|
15
|
+
prehistoric (Log4r) or messed up and insufficient (Standard library logger, logging library, etc.)
|
16
|
+
|
17
|
+
Q: **What's so special about Gallus?**<br />
|
18
|
+
A: Nothing really, it's just a collection of best practices from loggers of different technologies (like Log4j & Slf4j, Python logging, etc.)
|
19
|
+
|
20
|
+
Q: **So why would I use Gallus over Log4r for example.**<br />
|
21
|
+
A: Because it's simpler to customize, it's more powerful by default, it's more robust in working with context variables.
|
22
|
+
|
23
|
+
Q: **Is it faster than Log4r or standard logger?**<br />
|
24
|
+
A: It is not. It's about 30-60% slower depending on the log level (check `hacking/benchmarks.rb` for comparison with Log4r).
|
25
|
+
|
26
|
+
Q: **What? It's slower even than Log4r, why would I event want to use it?**<br />
|
27
|
+
A: Log4r provides much more limited way to work with contexts, and this is where the overhead comes from. If you'd wrap Log4r with adapter that works the same way as Gallus, performance would be the same. On a side note, if you consider performance of logging as your bottleneck then you evidently do something wrong.
|
28
|
+
|
29
|
+
Q: **Yeah? So logger can be slow?**<br />
|
30
|
+
A: Gallus ain't no slow, it's slower than Log4r, a tradeoff of convenience in usage. It's still blazingly fast comparing with all your business logic operations.
|
31
|
+
|
32
|
+
## Installation
|
33
|
+
|
34
|
+
Add this line to your application's Gemfile:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
gem 'gallus', '0.1.0'
|
38
|
+
```
|
39
|
+
|
40
|
+
And then execute:
|
41
|
+
|
42
|
+
$ bundle
|
43
|
+
|
44
|
+
Or install it yourself as:
|
45
|
+
|
46
|
+
$ gem install gallus --version 0.1.0
|
47
|
+
|
48
|
+
## Usage
|
49
|
+
|
50
|
+
Start off from configuring root logger:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
require 'gallus'
|
54
|
+
|
55
|
+
Gallus::Log.configure do |log|
|
56
|
+
log.level = :INFO
|
57
|
+
log.output << Gallus::Output::Stderr.new(Gallus::Format::SimpleLog.new)
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
Simple logging:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
Gallus::Log.root.info("Hello, this is info message")
|
65
|
+
# => I @ 2016-01-15T16:32:56+01:00Z $ root > Hello, this is info message
|
66
|
+
|
67
|
+
Gallus::Log.root.info("With context", foo: 1, bar: "baz")
|
68
|
+
# => I @ 2016-01-15T16:32:56+01:00Z $ root > With context; foo=1 bar="baz"
|
69
|
+
|
70
|
+
Gallus::Log.root.info("With lazy context", foo: 1, bar: -> { 100 * 2 })
|
71
|
+
# => I @ 2016-01-15T16:32:56+01:00Z $ root > With context; foo=1 bar=200
|
72
|
+
```
|
73
|
+
|
74
|
+
Nothing fancy so far. Lets try injecting loggers into classes:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
class User < Struct.new(:name)
|
78
|
+
include Gallus::Logging
|
79
|
+
|
80
|
+
def greet
|
81
|
+
log.info("Greeted", name: name)
|
82
|
+
"Hello, I'm #{name}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
User.new("Jon Snow").greet
|
87
|
+
# => I @ 2016-01-15T16:32:56+01:00Z $ User > Greeted; name="Jon Snow"
|
88
|
+
```
|
89
|
+
|
90
|
+
Wow, did you see that? What did just happened? We have logger injected and configured with one `include`.
|
91
|
+
And those neat context variables:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
log.info("Multi level contexts", foo: 1, bar: 2)
|
95
|
+
# => I @ 2016-01-15T16:32:56+01:00Z $ root > Multi level context; foo=1 bar=2
|
96
|
+
```
|
97
|
+
|
98
|
+
What about global contexts?
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
Gallus::Log.global_context { |ctx| ctx[:location] = "Castle Black" }
|
102
|
+
|
103
|
+
SamwellTarly.log.info("I always wanted to be a Wizard", name: "Samwell Tarly")
|
104
|
+
# => I always wanted to be a Wizard; name="Samwell Tarly" location="Castle Black"
|
105
|
+
|
106
|
+
JonSnow.log.info("I know nothing", name: "Jon Snow")
|
107
|
+
# => I know nothing; name="Jon Snow" location="Castle Black"
|
108
|
+
```
|
109
|
+
|
110
|
+
Q: Why block in `global_context` call?<br/>
|
111
|
+
A: Because it's thread-safe this way.
|
112
|
+
|
113
|
+
Speaking of threads...
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
t1 = Thread.new do
|
117
|
+
Gallus::Log.current_thread_context { |ctx| ctx[:location] = "Castle Black" }
|
118
|
+
SamwellTarly.log.info("I always wanted to be a Wizard", name: "Samwell Tarly")
|
119
|
+
end
|
120
|
+
|
121
|
+
t2 = Thread.new do
|
122
|
+
Gallus::Log.current_thread_context { |ctx| ctx[:location] = "Beyond the Wall" }
|
123
|
+
JonSnow.log.info("I know nothing", name: "Jon Snow")
|
124
|
+
end
|
125
|
+
|
126
|
+
t1.join
|
127
|
+
t2.join
|
128
|
+
|
129
|
+
# => I always wanted to be a Wizard; name="Samwell Tarly" location="Castle Black"
|
130
|
+
# => I know nothing; name="Jon Snow" location="Beyond the Wall"
|
131
|
+
```
|
132
|
+
|
133
|
+
Yeap, you can have context variables per thread too.
|
134
|
+
|
135
|
+
### Hacking
|
136
|
+
|
137
|
+
Finally, you can customize pretty much everything here. Output, serialization and formatting handlers are
|
138
|
+
all callables (Proc interfaces). So you can do something like this:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
|
142
|
+
Gallus::Log.configure do |log|
|
143
|
+
custom_format = -> (event) { "#{event.level} (#{event.payload[:pid]}): #{event.message}" }
|
144
|
+
log.output << Gallus::Output::Stderr.new(custom_format)
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
Or even like this:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
Gallus::Log.configure do |log|
|
152
|
+
log.output << -> (event) { puts event.inspect }
|
153
|
+
end
|
154
|
+
```
|
155
|
+
|
156
|
+
### Configuration inheritance
|
157
|
+
|
158
|
+
Given logger `Foo` and `Foo::Bar`, and `Foo::Bar::Baz` - `Foo` inherits from `root`, `Foo::Bar` from `Foo`, etc...
|
159
|
+
You can override configuration manually:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
Gallus::Log.configure do |log|
|
163
|
+
log.level = :INFO
|
164
|
+
end
|
165
|
+
|
166
|
+
Gallus::Log.configure("Foo") do |log|
|
167
|
+
log.level = :ERROR
|
168
|
+
end
|
169
|
+
|
170
|
+
Gallus::Log.configure("Foo::Bar") do |log|
|
171
|
+
log.level = :DEBUG
|
172
|
+
end
|
173
|
+
```
|
174
|
+
|
175
|
+
**NOTE**: Configuration must be executed before creation of the logger. At this point configuration is frozen and
|
176
|
+
child loggers can't be reconfigured. You can reconfigure particular logger though.
|
177
|
+
|
178
|
+
## Development
|
179
|
+
|
180
|
+
You have two options to work with this project. The [docker flow](#setup-with-docker) is suggested since solves problems of compatibility of tools.
|
181
|
+
|
182
|
+
### Manual Setup
|
183
|
+
|
184
|
+
First off, make sure you have **Ruby 2.2+** and latest version of **Bundler** on your machine. After checking out the repo, you can install dependencies and prepare the project with:
|
185
|
+
|
186
|
+
$ bin/setup
|
187
|
+
|
188
|
+
Now you can run tests:
|
189
|
+
|
190
|
+
$ bundle exec rake spec
|
191
|
+
|
192
|
+
You can also connect to interactive prompt that will allow you to experiment. To do this, run:
|
193
|
+
|
194
|
+
$ bundle exec bin/console
|
195
|
+
|
196
|
+
To install this gem onto your local machine, run:
|
197
|
+
|
198
|
+
$ bundle exec rake install
|
199
|
+
|
200
|
+
To run all example files, use following rake task:
|
201
|
+
|
202
|
+
$ bundle exec rake examples
|
203
|
+
|
204
|
+
### Setup with Docker
|
205
|
+
|
206
|
+
If you're lazy and don't wanna get into how the setup works, here's something for you. This project comes fully [dockerized](http://docker.io/). Install docker toolchain and then go for:
|
207
|
+
|
208
|
+
$ docker-compose build
|
209
|
+
|
210
|
+
All done, you can do testing and fiddling around:
|
211
|
+
|
212
|
+
$ docker-compose run gallus bash
|
213
|
+
root@xyyyyxx:/usr/local/src/gallus# bundle exec rake spec
|
214
|
+
root@xyyyyxx:/usr/local/src/gallus# bundle exec bin/console
|
215
|
+
|
216
|
+
### Releasing new version
|
217
|
+
|
218
|
+
This project is powered by rake-bump. To release gem version, follow [this continuous releasing guide](https://github.com/jobandtalent/rake-bump#continuous-releasing).
|
219
|
+
|
220
|
+
**NOTE**: This gem is a dependency for `rake-bump`, so to avoid circular dependency issues you should invoke bump tasks follow:
|
221
|
+
|
222
|
+
$ bundle exec rake -r rake/bump/tasks bump
|
223
|
+
$ bundle exec rake -r rake/bump/tasks release:rubygems
|
224
|
+
|
225
|
+
## Contributing
|
226
|
+
|
227
|
+
Bug reports and pull requests are welcome [here](https://github.com/jobandtalent/gallus/issues).
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "rake/testtask"
|
2
|
+
|
3
|
+
if defined?(Rake::Bump)
|
4
|
+
require File.expand_path("../lib/gallus/version", __FILE__)
|
5
|
+
|
6
|
+
Rake::Bump::Tasks.new do |t|
|
7
|
+
t.gem_name = 'gallus'
|
8
|
+
t.gem_current_version = Gallus::VERSION
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
13
|
+
t.libs << "test"
|
14
|
+
t.libs << "lib"
|
15
|
+
t.test_files = FileList['test/**/*_test.rb']
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Run all examples in sequence"
|
19
|
+
task :examples do
|
20
|
+
Pathname.new(Dir.pwd).join('examples').each_child do |f|
|
21
|
+
puts "-----> Executing example: #{f.basename}"
|
22
|
+
system("ruby #{f.to_s}") if f.file?
|
23
|
+
puts
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
task :default => :test
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "gallus"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/docker-compose.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'gallus'
|
2
|
+
|
3
|
+
include Gallus
|
4
|
+
|
5
|
+
Log.configure do |log|
|
6
|
+
log.output << Output::Stderr.new(Format::SimpleLog.new)
|
7
|
+
end
|
8
|
+
|
9
|
+
class SamwellTarly
|
10
|
+
include Logging
|
11
|
+
end
|
12
|
+
|
13
|
+
class JonSnow
|
14
|
+
include Logging
|
15
|
+
end
|
16
|
+
|
17
|
+
t1 = Thread.new do
|
18
|
+
Log.current_thread_context { |ctx| ctx[:location] = "Castle Black" }
|
19
|
+
SamwellTarly.log.info("I always wanted to be a Wizard", name: "Samwell Tarly")
|
20
|
+
end
|
21
|
+
|
22
|
+
t2 = Thread.new do
|
23
|
+
Log.current_thread_context { |ctx| ctx[:location] = "Beyond the Wall" }
|
24
|
+
JonSnow.log.info("I know nothing", name: "Jon Snow")
|
25
|
+
end
|
26
|
+
|
27
|
+
t1.join
|
28
|
+
t2.join
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'gallus'
|
2
|
+
|
3
|
+
include Gallus
|
4
|
+
|
5
|
+
logger = Log.configure('test') do |log|
|
6
|
+
custom_format = -> (event) { "#{event.level} (#{event.payload[:pid]}): #{event.message}" }
|
7
|
+
log.output << Output::Stderr.new(custom_format)
|
8
|
+
end
|
9
|
+
|
10
|
+
logger.info('Custom format here...', pid: Process.pid)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'gallus'
|
2
|
+
|
3
|
+
include Gallus
|
4
|
+
|
5
|
+
logger = Log.configure('test') do |log|
|
6
|
+
custom_json_serialization = -> (event) { event.to_json }
|
7
|
+
log.output << Output::Stderr.new(Format::SimpleLog.new(custom_json_serialization))
|
8
|
+
end
|
9
|
+
|
10
|
+
logger.info('Context as JSON again, but custom serialization used this time', foo: 1, bar: 'baz')
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'gallus'
|
2
|
+
|
3
|
+
include Gallus
|
4
|
+
|
5
|
+
Log.configure do |log|
|
6
|
+
log.output << Output::Stderr.new(Format::SimpleLog.new)
|
7
|
+
end
|
8
|
+
|
9
|
+
class SamwellTarly
|
10
|
+
include Logging
|
11
|
+
end
|
12
|
+
|
13
|
+
class JonSnow
|
14
|
+
include Logging
|
15
|
+
end
|
16
|
+
|
17
|
+
Log.global_context { |ctx| ctx[:location] = "Castle Black" }
|
18
|
+
SamwellTarly.log.info("I always wanted to be a Wizard", name: "Samwell Tarly")
|
19
|
+
JonSnow.log.info("I know nothing", name: "Jon Snow")
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'gallus'
|
2
|
+
|
3
|
+
include Gallus
|
4
|
+
|
5
|
+
logger = Log.configure('test') do |log|
|
6
|
+
log.level = :DEBUG
|
7
|
+
log.output << Output::Stderr.new(Format::SimpleConsole.new)
|
8
|
+
end
|
9
|
+
|
10
|
+
logger.info('This is console output')
|
11
|
+
logger.warn('Everything else than INFO is being prefixed')
|
12
|
+
logger.debug('Checking debugs, just in case')
|
13
|
+
logger.error('And errors too...')
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'gallus'
|
2
|
+
|
3
|
+
include Gallus
|
4
|
+
|
5
|
+
logger = Log.configure('test') do |log|
|
6
|
+
log.level = :DEBUG
|
7
|
+
log.output << Output::Stderr.new(Format::SimpleLog.new)
|
8
|
+
end
|
9
|
+
|
10
|
+
logger.info('Hello World')
|
11
|
+
logger.warn('Not everything is what it seems', planet: 'Earth')
|
12
|
+
logger.info('Another info message with context', foo: 1, bar: 2)
|
data/gallus.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gallus/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "gallus"
|
8
|
+
spec.version = Gallus::VERSION
|
9
|
+
spec.authors = ["jobandtalent", "Kris Kovalik"]
|
10
|
+
spec.email = ["kris.kovalik@jobandtalent.com", "hi@kkvlk.me"]
|
11
|
+
|
12
|
+
spec.summary = %q{Production grade logger for Ruby.}
|
13
|
+
spec.description = %q{Easy to use and insanely powerfull logger for Ruby applications.}
|
14
|
+
spec.homepage = "https://github.com/jobandtalent/gallus"
|
15
|
+
spec.license = 'Apache-2.0'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "minitest", "~> 5.8"
|
25
|
+
spec.add_development_dependency "minitest-reporters", "~> 1.1"
|
26
|
+
spec.add_development_dependency "mocha", "~> 1.1"
|
27
|
+
spec.add_development_dependency "log4r", "~> 1.1", ">= 1.0"
|
28
|
+
#spec.add_development_dependency "rake-bump", "~> 0.4", ">= 0.4.6"
|
29
|
+
|
30
|
+
spec.add_dependency "activesupport", "~> 4.2", ">= 3.0"
|
31
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Usage: LEVEL=INFO N=10000 bundle exec ruby hacking/benchmarks.rb
|
2
|
+
|
3
|
+
require 'log4r'
|
4
|
+
require 'gallus'
|
5
|
+
require 'stringio'
|
6
|
+
require 'benchmark'
|
7
|
+
|
8
|
+
LEVEL = ENV.fetch('LEVEL', 'INFO')
|
9
|
+
|
10
|
+
$gallus = Gallus::Log.configure('test') do |log|
|
11
|
+
log.level = LEVEL
|
12
|
+
log.output << Gallus::Output::Stream.new(StringIO.new, Gallus::Format::SimpleLog.new)
|
13
|
+
end
|
14
|
+
|
15
|
+
N = ENV.fetch('N', 10000).to_i
|
16
|
+
calls = []
|
17
|
+
|
18
|
+
N.times do
|
19
|
+
%w[debug info warn error].each do |level|
|
20
|
+
calls << level
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Log4r::IOOutputter.new('stream', StringIO.new, formatter: Log4r::PatternFormatter.new(pattern: "%l @ %d $ %C - %m"))
|
25
|
+
|
26
|
+
$log4r = Log4r::Logger.new('test')
|
27
|
+
$log4r.level = Log4r.const_get(LEVEL)
|
28
|
+
$log4r.add('stream')
|
29
|
+
|
30
|
+
payload = { one: 1, two: "Two", three: Object.new, four: "Yada! yada! yada!" }
|
31
|
+
|
32
|
+
Benchmark.bm do |x|
|
33
|
+
x.report("Log4r: ") do
|
34
|
+
calls.dup.shuffle.each do |level|
|
35
|
+
$log4r.send(level) do
|
36
|
+
formatted_payload = payload.map { |k,v| "#{k}=#{v.inspect}" }.join(" ")
|
37
|
+
"This is fancy little message; #{formatted_payload}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
x.report("Gallus: ") do
|
43
|
+
calls.dup.shuffle.each do |level|
|
44
|
+
$gallus.send(level, "This is fancy little message", payload)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/gallus/event.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Gallus
|
2
|
+
# Internal: Every log event is wrapped with this envelope. It holds information about the logger that
|
3
|
+
# performed the operation, log level, message (or object passed) and full payload. Also includes information
|
4
|
+
# at what time event has been recorded.
|
5
|
+
Event = Struct.new(:logger, :level, :message, :payload) do
|
6
|
+
attr_reader :recorded_at
|
7
|
+
|
8
|
+
def initialize(*)
|
9
|
+
super
|
10
|
+
|
11
|
+
self.payload = Payload.new(payload || {})
|
12
|
+
@recorded_at = Time.now.utc
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Gallus
|
2
|
+
module Format
|
3
|
+
# Public: This console log format is used for command line apps. Instead of using puts-es and write-s to
|
4
|
+
# display stuff use logger! INFO level will be displayed without prefixes, as is - just message and context
|
5
|
+
# info. Other levels will be prefixed with level name. Example:
|
6
|
+
#
|
7
|
+
# Hello, this is info message
|
8
|
+
# ERROR: Upps, something went wrong; foo="Bar"
|
9
|
+
# Another info message
|
10
|
+
# DEBUG: Here's debug information
|
11
|
+
# ...
|
12
|
+
#
|
13
|
+
class SimpleConsole
|
14
|
+
def initialize(serialization = Serialization::Inspect)
|
15
|
+
@serialization = serialization
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(event)
|
19
|
+
parts = [ [ event.message, @serialization.call(event.payload) ].compact.join('; ') ]
|
20
|
+
parts.unshift(format("%s:", event.level.name)) unless event.level == Level::INFO
|
21
|
+
parts.compact.join(' ')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Gallus
|
2
|
+
module Format
|
3
|
+
# Public: Simple because of implementation, not because of the output. It's default log line that contains
|
4
|
+
# most information needed. Everything presented in a compact and easy to parse form. Example:
|
5
|
+
#
|
6
|
+
# "I @ 2016-01-15T16:32:56+01:00Z $ Foo > Hello World; foo=1 bar=2"
|
7
|
+
class SimpleLog
|
8
|
+
def initialize(serialization = Serialization::Inspect)
|
9
|
+
@serialization = serialization
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(event)
|
13
|
+
message = [ event.message, @serialization.call(event.payload) ].compact.join('; ')
|
14
|
+
format("%s @ %s $ %s > %s", event.level.name[0], event.recorded_at.iso8601, event.logger, message)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/gallus/level.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Gallus
|
2
|
+
# Internal: Log level represented in a coherent way. You can easily compare log levels between each other.
|
3
|
+
# You're also able to get log level by string/symbol name.
|
4
|
+
class Level
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
# Internal: All log levels.
|
8
|
+
def self.all
|
9
|
+
@all ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
# Internal: Shorthand to all.each.
|
13
|
+
def self.each(&block)
|
14
|
+
self.all.each(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Internal: Returns level defined under given name.
|
18
|
+
def self.[](name)
|
19
|
+
const_get(name.to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :name, :id
|
23
|
+
|
24
|
+
# Internal: Constructor. Initializes logger with given name and identifier, then registers it.
|
25
|
+
def initialize(name, id)
|
26
|
+
@name, @id = name.to_s, id
|
27
|
+
|
28
|
+
self.class.const_set(@name, self)
|
29
|
+
self.class.all << self
|
30
|
+
end
|
31
|
+
|
32
|
+
def <=>(other)
|
33
|
+
self.id <=> other.id
|
34
|
+
rescue => err
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
name
|
40
|
+
end
|
41
|
+
|
42
|
+
# Register available log levels:
|
43
|
+
|
44
|
+
new :TRACE, 1
|
45
|
+
new :DEBUG, 2
|
46
|
+
new :INFO, 3
|
47
|
+
new :WARN, 4
|
48
|
+
new :ERROR, 5
|
49
|
+
end
|
50
|
+
end
|
data/lib/gallus/log.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
module Gallus
|
2
|
+
# Public: The brain of logging. All defined loggers inherit from this class.
|
3
|
+
class Log
|
4
|
+
# Global context requires a semaphore to be thread-safe.
|
5
|
+
@@global_context_mutex = Mutex.new
|
6
|
+
|
7
|
+
# Global context stores context variables that will be printed by any logger alongside with its
|
8
|
+
# local information.
|
9
|
+
@@global_context = {}
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# Public: Configures logger with given name. When no name specified, root logger will be configured.
|
13
|
+
#
|
14
|
+
# Example:
|
15
|
+
#
|
16
|
+
# Gallus::Log.configure do |log|
|
17
|
+
# log.level = :INFO
|
18
|
+
# log.output << -> (event) { puts event.inspect }
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Returns configured logger.
|
22
|
+
def configure(name = '', &block)
|
23
|
+
Repository.get_or_create_logger(name, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Returns logger registered under given name.
|
27
|
+
def [](name)
|
28
|
+
configure(name)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Internal: For testing purposes we should be able to remove logger with given name.
|
32
|
+
def delete(name)
|
33
|
+
Repository.delete_with_children(name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Internal: Performance magic. It dynamically defines log level methods to avoid overhead of not-logged
|
37
|
+
# levels. For example if log level is WARN it'll define empty `trace`, `debug` and `info` methods and fully
|
38
|
+
# working `warn` and `error`. Effectively log methods call `log` under the hood.
|
39
|
+
def define_log_methods!(log_level)
|
40
|
+
Level.each do |level|
|
41
|
+
method_name = level.name.downcase
|
42
|
+
remove_method(method_name) if method_defined?(method_name)
|
43
|
+
|
44
|
+
if level >= log_level
|
45
|
+
define_method(method_name) do |message = nil, payload = {}, &block|
|
46
|
+
log(level, message, payload, &block)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
define_method(method_name) do |message = nil, payload = {}, &block|
|
50
|
+
# supressed...
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Public: Yields global context. Only block calls are allowed since this operation must be thread-safe.
|
57
|
+
#
|
58
|
+
# Example:
|
59
|
+
#
|
60
|
+
# log.global_context { |ctx| ctx[:location] = "Castle Black" }
|
61
|
+
# samwell_log.info("I always wanted to be a Wizard", name: "Samwell Tarly")
|
62
|
+
# # => I always wanted to be a Wizard; name="Samwell Tarly" location="Castle Black"
|
63
|
+
# jon_snow_log.info("I know nothing", name: "Jon Snow")
|
64
|
+
# # => I know nothing; name="Jon Snow" location="Castle Black"
|
65
|
+
#
|
66
|
+
def global_context
|
67
|
+
@@global_context_mutex.synchronize { yield @@global_context }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Public: Yields context for current thread. Just to conform with the behavior of #global_context we're
|
71
|
+
# using block call here as well.
|
72
|
+
#
|
73
|
+
# Example:
|
74
|
+
#
|
75
|
+
# log.current_thread_context { |ctx| ctx[:location] = "Castle Black" }
|
76
|
+
# log.info("I know nothing!", name: "Jon Snow")
|
77
|
+
# # => I know nothing! name="Jon Snow" location="Castle Black"
|
78
|
+
#
|
79
|
+
def current_thread_context
|
80
|
+
yield (Thread.current[:log_context] ||= {})
|
81
|
+
end
|
82
|
+
|
83
|
+
# Public: Returns root logger.
|
84
|
+
def root
|
85
|
+
@@root
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Public: Logger name, log level and context serializtion handler.
|
90
|
+
attr_accessor :name, :level
|
91
|
+
|
92
|
+
# Public: A list with configured outputs.
|
93
|
+
attr_reader :output
|
94
|
+
|
95
|
+
# Internal: Parent logger. Used for testing purposes.
|
96
|
+
attr_reader :parent
|
97
|
+
|
98
|
+
# Internal: Constructs new logger with configuration inherited from given parent. Yields itself so
|
99
|
+
# it can be reconfigured with a block. Don't use this method directly. Use Gallus::Log.configure class
|
100
|
+
# method if you wanna define or reconfigure a logger.
|
101
|
+
def initialize(parent, name, &block)
|
102
|
+
@parent, @name = parent, name.to_s
|
103
|
+
|
104
|
+
if parent
|
105
|
+
@output = @parent.output.dup
|
106
|
+
self.level = @parent.level
|
107
|
+
end
|
108
|
+
|
109
|
+
@output ||= []
|
110
|
+
|
111
|
+
yield self if block_given?
|
112
|
+
end
|
113
|
+
|
114
|
+
# Public: Changing log level means that we have to redefine logging methods.
|
115
|
+
def level=(level)
|
116
|
+
level = level.is_a?(Level) ? level : Level[level.to_s]
|
117
|
+
return if @level == level
|
118
|
+
self.class.class_eval { define_log_methods!(level) }
|
119
|
+
@level = level
|
120
|
+
end
|
121
|
+
|
122
|
+
# Public: Shortand to Gallus::Log.global_context.
|
123
|
+
def global_context(&block)
|
124
|
+
self.class.global_context(&block)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Public: Shortand to Gallus::Log.current_thread_context.
|
128
|
+
def current_thread_context(&block)
|
129
|
+
self.class.current_thread_context(&block)
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
# Internal: Merges data from all contexts and provides them as a single hash. Order:
|
135
|
+
#
|
136
|
+
# 1. Global context.
|
137
|
+
# 2. Curreant thread context.
|
138
|
+
# 3. Self context.
|
139
|
+
#
|
140
|
+
def merged_context
|
141
|
+
{}.tap do |merged|
|
142
|
+
self.class.global_context { |ctx| merged.merge!(ctx) }
|
143
|
+
self.class.current_thread_context { |ctx| merged.merge!(ctx) }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Internal: Actual logging. Takes payload and merges it into contexts, serializes context variables
|
148
|
+
# using configured serialization handler and finally sends it to each defined output.
|
149
|
+
def log(level, message = nil, payload = {}, &block)
|
150
|
+
message ||= block.call if block_given?
|
151
|
+
event = Event.new(@name, level, message.to_s, merged_context.merge(payload))
|
152
|
+
output.each { |out| out.call(event) }
|
153
|
+
end
|
154
|
+
|
155
|
+
# Root logger configuration (defaults for any logger defined after):
|
156
|
+
|
157
|
+
@@root = Log.configure do |log|
|
158
|
+
log.name = 'root'
|
159
|
+
log.level = Level::INFO
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Gallus
|
2
|
+
# Internal: Programmers are lazy, why to initialize logger manually in language like Ruby, when you can
|
3
|
+
# use handy one-liner include to have logger available for your class. Example:
|
4
|
+
#
|
5
|
+
# class Foo
|
6
|
+
# include Gallus::Logging
|
7
|
+
#
|
8
|
+
# def initialize
|
9
|
+
# log.debug("Initializing!")
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# Gallus::Log.configure('Foo') do |log|
|
14
|
+
# log.level = :DEBUG
|
15
|
+
# log.output << Gallus::Output::Stdout.new(Gallus::Format::SimpleConsole.new)
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# Foo.new # => "DEBUG: Initializing!"
|
19
|
+
#
|
20
|
+
module Logging
|
21
|
+
def self.included(klass)
|
22
|
+
klass.extend(ClassMethods)
|
23
|
+
end
|
24
|
+
|
25
|
+
def log
|
26
|
+
self.class.log
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
def log
|
31
|
+
@log ||= ::Gallus::Log.configure(self.name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Gallus
|
2
|
+
module Output
|
3
|
+
# Public: A thread safe IO stream output that writes to given file.
|
4
|
+
class File < Stream
|
5
|
+
def initialize(filename, format)
|
6
|
+
@file = ::File.open(filename, 'a+')
|
7
|
+
super(@file, format)
|
8
|
+
end
|
9
|
+
|
10
|
+
def close
|
11
|
+
@file.close
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Gallus
|
2
|
+
module Output
|
3
|
+
# Public: Simple, thread-safe IO stream output handler. Takes stream and format on input.
|
4
|
+
class Stream
|
5
|
+
def initialize(stream, format)
|
6
|
+
@mutex = Mutex.new
|
7
|
+
@stream, @format = stream, format
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(event)
|
11
|
+
@mutex.synchronize { call!(event) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def call!(event)
|
15
|
+
@stream.write(@format.call(event) + "\n")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Gallus
|
2
|
+
# Public: Similarly to Gallus::Logging no point to initialize global logger for your
|
3
|
+
# modules/packages. Include this mixin to define LOG constant and log class method shortcut.
|
4
|
+
#
|
5
|
+
# Often used practice is to define such top level logger as a parent with default outputters,
|
6
|
+
# formatters, log level, etc.
|
7
|
+
module PackageLogging
|
8
|
+
def self.included(klass)
|
9
|
+
klass.const_set(:LOG, ::Gallus::Log.configure(klass.name))
|
10
|
+
klass.extend(ClassMethods)
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def log
|
15
|
+
const_get(:LOG)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Gallus
|
2
|
+
# Internal: To improve performance, context can provide values wrapped in lambdas. Those will be
|
3
|
+
# executed at the time of writing log. Getting the values from lambdas is the job of this simple
|
4
|
+
# payload class.
|
5
|
+
class Payload
|
6
|
+
include Enumerable
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :@h, :empty?, :each
|
10
|
+
|
11
|
+
def initialize(payload)
|
12
|
+
@h = payload.inject({}) do |res,(k,v)|
|
13
|
+
res[k.to_sym] = v.is_a?(Proc) ? v.call : v
|
14
|
+
res
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
@h
|
20
|
+
end
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
@h.map { |k,v| "#{k}=#{v.inspect}" }.join(" ")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Gallus
|
2
|
+
# Internal: Wee need a place to keep track of registered loggers and their parents. This little
|
3
|
+
# repository class handles this task in a thread-safe manner.
|
4
|
+
class Repository
|
5
|
+
PARENT_DELIMITER = '::'
|
6
|
+
|
7
|
+
@mutex = Mutex.new
|
8
|
+
|
9
|
+
# Internal: Returns all registered loggers.
|
10
|
+
def self.all
|
11
|
+
@all ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Internal: There must be a way to find parent logger for given class name. For example, looking up parent
|
15
|
+
# for Foo::Bar::Baz logger it'll look up for Foo::Bar, then falling back to Foo and eventually to root logger
|
16
|
+
# if nothing found.
|
17
|
+
def self.find_parent(name)
|
18
|
+
parent, name = nil, name.dup
|
19
|
+
|
20
|
+
while parent.nil?
|
21
|
+
name = name.split(PARENT_DELIMITER)[0..-2].join(PARENT_DELIMITER)
|
22
|
+
parent = all[name]
|
23
|
+
break if name.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
parent
|
27
|
+
end
|
28
|
+
|
29
|
+
# Internal: For testing purposes we need to be able to delete logger alongside with all its children.
|
30
|
+
def self.delete_with_children(name)
|
31
|
+
@mutex.synchronize do
|
32
|
+
all.keys.each { |k| all.delete(k) if k.start_with?(name + PARENT_DELIMITER) }
|
33
|
+
all.delete(name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Internal: We obviously need a way to create logger or retrieve it by name if already registered.
|
38
|
+
# Creation of logger causes configuration to be inherited from parent (or root) logger.
|
39
|
+
def self.get_or_create_logger(name, &block)
|
40
|
+
name, log = name.to_s, nil
|
41
|
+
|
42
|
+
@mutex.synchronize do
|
43
|
+
if log = all[name]
|
44
|
+
yield log if block_given?
|
45
|
+
else
|
46
|
+
all[name] = (log = Class.new(Log).new(find_parent(name), name, &block))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
log
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/gallus.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module Gallus
|
5
|
+
require 'gallus/version'
|
6
|
+
require 'gallus/format/simple_console'
|
7
|
+
require 'gallus/format/simple_log'
|
8
|
+
require 'gallus/output/null'
|
9
|
+
require 'gallus/output/stream'
|
10
|
+
require 'gallus/output/stdout'
|
11
|
+
require 'gallus/output/stderr'
|
12
|
+
require 'gallus/output/file'
|
13
|
+
require 'gallus/serialization/inspect'
|
14
|
+
require 'gallus/serialization/json'
|
15
|
+
require 'gallus/level'
|
16
|
+
require 'gallus/payload'
|
17
|
+
require 'gallus/event'
|
18
|
+
require 'gallus/repository'
|
19
|
+
require 'gallus/log'
|
20
|
+
require 'gallus/logging'
|
21
|
+
require 'gallus/package_logging'
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gallus
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- jobandtalent
|
8
|
+
- Kris Kovalik
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-01-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.10'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.10'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '10.0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '10.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: minitest
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '5.8'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '5.8'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: minitest-reporters
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.1'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.1'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: mocha
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '1.1'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '1.1'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: log4r
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '1.1'
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '1.0'
|
94
|
+
type: :development
|
95
|
+
prerelease: false
|
96
|
+
version_requirements: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - "~>"
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '1.1'
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.0'
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: activesupport
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '4.2'
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '3.0'
|
114
|
+
type: :runtime
|
115
|
+
prerelease: false
|
116
|
+
version_requirements: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - "~>"
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '4.2'
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '3.0'
|
124
|
+
description: Easy to use and insanely powerfull logger for Ruby applications.
|
125
|
+
email:
|
126
|
+
- kris.kovalik@jobandtalent.com
|
127
|
+
- hi@kkvlk.me
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".editorconfig"
|
133
|
+
- ".gitignore"
|
134
|
+
- CHANGELOG.md
|
135
|
+
- Dockerfile
|
136
|
+
- Gemfile
|
137
|
+
- README.md
|
138
|
+
- Rakefile
|
139
|
+
- bin/console
|
140
|
+
- bin/setup
|
141
|
+
- docker-compose.yml
|
142
|
+
- examples/current_thread_context.rb
|
143
|
+
- examples/custom_format.rb
|
144
|
+
- examples/custom_output.rb
|
145
|
+
- examples/custom_serialization.rb
|
146
|
+
- examples/global_context.rb
|
147
|
+
- examples/json_serialization.rb
|
148
|
+
- examples/lambda_context_vars.rb
|
149
|
+
- examples/simple_console.rb
|
150
|
+
- examples/simple_log.rb
|
151
|
+
- gallus.gemspec
|
152
|
+
- hacking/benchmarks.rb
|
153
|
+
- lib/gallus.rb
|
154
|
+
- lib/gallus/event.rb
|
155
|
+
- lib/gallus/format/simple_console.rb
|
156
|
+
- lib/gallus/format/simple_log.rb
|
157
|
+
- lib/gallus/level.rb
|
158
|
+
- lib/gallus/log.rb
|
159
|
+
- lib/gallus/logging.rb
|
160
|
+
- lib/gallus/output/file.rb
|
161
|
+
- lib/gallus/output/null.rb
|
162
|
+
- lib/gallus/output/stderr.rb
|
163
|
+
- lib/gallus/output/stdout.rb
|
164
|
+
- lib/gallus/output/stream.rb
|
165
|
+
- lib/gallus/package_logging.rb
|
166
|
+
- lib/gallus/payload.rb
|
167
|
+
- lib/gallus/repository.rb
|
168
|
+
- lib/gallus/serialization/inspect.rb
|
169
|
+
- lib/gallus/serialization/json.rb
|
170
|
+
- lib/gallus/version.rb
|
171
|
+
homepage: https://github.com/jobandtalent/gallus
|
172
|
+
licenses:
|
173
|
+
- Apache-2.0
|
174
|
+
metadata: {}
|
175
|
+
post_install_message:
|
176
|
+
rdoc_options: []
|
177
|
+
require_paths:
|
178
|
+
- lib
|
179
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
180
|
+
requirements:
|
181
|
+
- - ">="
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: '0'
|
184
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - ">="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: '0'
|
189
|
+
requirements: []
|
190
|
+
rubyforge_project:
|
191
|
+
rubygems_version: 2.5.1
|
192
|
+
signing_key:
|
193
|
+
specification_version: 4
|
194
|
+
summary: Production grade logger for Ruby.
|
195
|
+
test_files: []
|