l2meter 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +170 -20
- data/lib/l2meter/configuration.rb +10 -9
- data/lib/l2meter/emitter.rb +64 -10
- data/lib/l2meter/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 865b41102fd7ebcd25b127d1e1bdd4d3a777fa05
|
4
|
+
data.tar.gz: cfd0071e73ec174fda966428c1f9421f8db1758f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afebdeaec22a78f58b14b66156e0804a961c34f84b0cced488c0185d90acba204d9921bd904522a5a58720bc29ea6207432afd5ead81b3f758e20e609a083383
|
7
|
+
data.tar.gz: 1eb3117c0347549685dd6cb4b0ac4b85bf2d02656a533b928509c88467c6e9f8bb72ea736ec5d34ef09ef5a37d1eabafd3b7f21e3a5f77f9af63f4945877afd1
|
data/README.md
CHANGED
@@ -1,35 +1,185 @@
|
|
1
1
|
# L2meter
|
2
|
+
[](https://rubygems.org/gems/l2meter)
|
3
|
+
[](http://travis-ci.org/heroku/l2meter)
|
4
|
+
[](https://codeclimate.com/github/heroku/l2meter)
|
2
5
|
|
3
|
-
L2meter is a little gem that helps you build loggers that outputs things in
|
6
|
+
L2meter is a little gem that helps you build loggers that outputs things in
|
7
|
+
l2met-friendly format.
|
4
8
|
|
5
|
-
###
|
9
|
+
### Basics
|
10
|
+
|
11
|
+
A new logger might be created like so:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
metrics = L2meter.build
|
15
|
+
```
|
16
|
+
|
17
|
+
If you plan to use it globally across different components of your app,consider
|
18
|
+
making it constant.
|
19
|
+
|
20
|
+
The base `log` method accepts two type of things: bare values and key-value
|
21
|
+
pairs in form of hashes.
|
6
22
|
|
7
23
|
```ruby
|
8
|
-
|
9
|
-
|
10
|
-
|
24
|
+
metrics.log "Hello world" # => hello-world
|
25
|
+
metrics.log :db_query, result: :success # => db-query result=success
|
26
|
+
```
|
27
|
+
|
28
|
+
It can also take a block. In this case the message will be emitted twice, once
|
29
|
+
at the start of the execution and another at the end. The end result might look
|
30
|
+
like so:
|
11
31
|
|
12
|
-
|
13
|
-
|
32
|
+
```ruby
|
33
|
+
metrics.log :doing_work do # => doing-work at=start
|
34
|
+
do_some_work #
|
35
|
+
metrics.log :work_done # => work-done
|
36
|
+
end # => doing-work at=finish elapsed=1.2345s
|
37
|
+
```
|
14
38
|
|
15
|
-
|
16
|
-
|
17
|
-
|
39
|
+
In case the exception is raised inside the block, l2meter will report is like
|
40
|
+
so:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
metrics.log :doing_work do # => doing-work
|
44
|
+
raise ArgumentError, \ #
|
45
|
+
"something is wrong" #
|
46
|
+
end # => doing-work at=exception exception=ArgumentError message="something is wrong" elapsed=1.2345s
|
47
|
+
```
|
48
|
+
|
49
|
+
## Context
|
50
|
+
|
51
|
+
L2meter allows setting context for a block. It might work something like this:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
def do_work_with_retries
|
55
|
+
attempt = 1
|
56
|
+
begin
|
57
|
+
metrics.context attempt: attempt do
|
58
|
+
do_some_work # => doing-work attempt=1
|
59
|
+
# => doing-work attempt=2
|
60
|
+
# => doing-work attempt=3
|
61
|
+
end
|
62
|
+
rescue => error
|
63
|
+
attempt += 1
|
64
|
+
retry
|
18
65
|
end
|
66
|
+
end
|
67
|
+
```
|
19
68
|
|
20
|
-
|
21
|
-
|
69
|
+
L2meter supports dynamic contexts as well. You can pass a proc instead of raw
|
70
|
+
value in porder to use it.
|
71
|
+
|
72
|
+
The same example as above could be written like ths instead:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
def do_work_with_retries
|
76
|
+
attempt = 1
|
77
|
+
metrics.context ->{{ attempt: attempt }} do
|
78
|
+
begin
|
79
|
+
do_some_work
|
80
|
+
rescue => error
|
81
|
+
attempt +=1
|
82
|
+
retry
|
83
|
+
end
|
84
|
+
end
|
22
85
|
end
|
86
|
+
```
|
87
|
+
|
88
|
+
## Other
|
89
|
+
|
90
|
+
Some other l2met-specific methods are supported.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
metrics.count :user_registered # => count#user-registered=1
|
94
|
+
metrics.count :registered_users, 10 # => count#registered-users=10
|
23
95
|
|
24
|
-
|
96
|
+
metrics.measure :connection_count, 20, # => measure#connection-count=235
|
97
|
+
metrics.measure :db_query, 235, unit: :ms, # => measure#db-query.ms=235
|
98
|
+
|
99
|
+
metrics.sample :connection_count, 20, # => sample#connection-count=235
|
100
|
+
metrics.sample :db_query, 235, unit: :ms, # => sample#db-query.ms=235
|
101
|
+
|
102
|
+
metrics.unique :user, "bob@example.com" # => unique#user=bob@example.com
|
103
|
+
```
|
104
|
+
|
105
|
+
### Configuration
|
106
|
+
|
107
|
+
L2meter supports configurtion. Here's how you cna configure things:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
metrics = L2meter.build do |config|
|
111
|
+
# configuration happen here
|
112
|
+
end
|
113
|
+
```
|
25
114
|
|
26
|
-
|
115
|
+
These are available configuration:
|
27
116
|
|
28
|
-
|
29
|
-
do_some_work #
|
30
|
-
end # => doing-work at=finish elapsed=3.1234s
|
117
|
+
#### Global context
|
31
118
|
|
32
|
-
|
33
|
-
|
34
|
-
|
119
|
+
Global context works similary to context method, but globally:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
config.context = { app_name: "my-app-name" }
|
123
|
+
|
124
|
+
# ...
|
125
|
+
|
126
|
+
metrics.log foo: :bar # => app-name=my-app-name foo-bar
|
127
|
+
```
|
128
|
+
|
129
|
+
Dynamic context is also supported:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
context.context do
|
133
|
+
{ request_id: CurrentContext.request_id }
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
137
|
+
#### Sorting
|
138
|
+
|
139
|
+
By default l2meter doesn't sort tokens before output, putting them in the order
|
140
|
+
they're passed. But you can make it sorted like so:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
config.sort = true
|
144
|
+
|
145
|
+
# ...
|
146
|
+
|
147
|
+
metrics.log :c, :b, :a # => a b c
|
148
|
+
```
|
149
|
+
|
150
|
+
#### Source
|
151
|
+
|
152
|
+
Source is a special parameter that'll be appended to all emitted messages.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
config.source = "production"
|
156
|
+
|
157
|
+
# ...
|
158
|
+
|
159
|
+
metrics.log foo: :bar # => source=production foo=bar
|
160
|
+
```
|
161
|
+
|
162
|
+
## Silence
|
163
|
+
|
164
|
+
There's a way to temporary silence the log emitter. This might be userful for
|
165
|
+
tests for example.
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
metrics.silence do
|
169
|
+
# logger is completely silenced
|
170
|
+
metrics.log "hello world" # nothing is emitted here
|
171
|
+
end
|
172
|
+
|
173
|
+
# works normally again
|
174
|
+
metrics.log :foo # => foo
|
175
|
+
```
|
176
|
+
|
177
|
+
The typical setup for RSpec might look like this:
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
RSpec.configure do |config|
|
181
|
+
config.around :each do |example|
|
182
|
+
Metrics.silence &example
|
183
|
+
end
|
184
|
+
end
|
35
185
|
```
|
@@ -1,6 +1,12 @@
|
|
1
1
|
module L2meter
|
2
2
|
class Configuration
|
3
3
|
attr_writer :output
|
4
|
+
attr_accessor :source
|
5
|
+
attr_reader :contexts
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@contexts = []
|
9
|
+
end
|
4
10
|
|
5
11
|
def output
|
6
12
|
@output ||= $stdout
|
@@ -8,7 +14,7 @@ module L2meter
|
|
8
14
|
|
9
15
|
def key_formatter
|
10
16
|
@key_formatter ||= ->(key) do
|
11
|
-
key.to_s.strip.downcase.gsub(/[^-a-z\d]+/, ?-)
|
17
|
+
key.to_s.strip.downcase.gsub(/[^-a-z\d.#]+/, ?-)
|
12
18
|
end
|
13
19
|
end
|
14
20
|
|
@@ -36,16 +42,11 @@ module L2meter
|
|
36
42
|
end
|
37
43
|
|
38
44
|
def context(&block)
|
39
|
-
@
|
40
|
-
end
|
41
|
-
|
42
|
-
def context=(value)
|
43
|
-
@static_context = value
|
45
|
+
@contexts = [block]
|
44
46
|
end
|
45
47
|
|
46
|
-
def
|
47
|
-
|
48
|
-
@context_block ? @context_block.call.to_h : {}
|
48
|
+
def context=(block_or_value)
|
49
|
+
@contexts = [block_or_value]
|
49
50
|
end
|
50
51
|
end
|
51
52
|
end
|
data/lib/l2meter/emitter.rb
CHANGED
@@ -6,10 +6,12 @@ module L2meter
|
|
6
6
|
@configuration = configuration
|
7
7
|
end
|
8
8
|
|
9
|
-
def log(*args
|
9
|
+
def log(*args)
|
10
|
+
params = Hash === args.last ? args.pop : {}
|
10
11
|
args = args.map { |key| [ key, true ] }.to_h
|
11
12
|
params = args.merge(params)
|
12
|
-
params =
|
13
|
+
params = current_context.merge(params)
|
14
|
+
params = merge_source(params)
|
13
15
|
|
14
16
|
if block_given?
|
15
17
|
wrap params, &Proc.new
|
@@ -26,23 +28,75 @@ module L2meter
|
|
26
28
|
configuration.output = output
|
27
29
|
end
|
28
30
|
|
31
|
+
def measure(metric, value, unit: nil)
|
32
|
+
metric = [metric, unit].compact * ?.
|
33
|
+
log_with_prefix :measure, metric, value
|
34
|
+
end
|
35
|
+
|
36
|
+
def sample(metric, value, unit: nil)
|
37
|
+
metric = [metric, unit].compact * ?.
|
38
|
+
log_with_prefix :sample, metric, value
|
39
|
+
end
|
40
|
+
|
41
|
+
def count(metric, value=1)
|
42
|
+
log_with_prefix :count, metric, value
|
43
|
+
end
|
44
|
+
|
45
|
+
def unique(metric, value)
|
46
|
+
log_with_prefix :unique, metric, value
|
47
|
+
end
|
48
|
+
|
49
|
+
def context(hash_or_proc)
|
50
|
+
configuration_contexts.push hash_or_proc
|
51
|
+
yield
|
52
|
+
ensure
|
53
|
+
configuration_contexts.pop
|
54
|
+
end
|
55
|
+
|
29
56
|
private
|
30
57
|
|
31
|
-
def
|
32
|
-
configuration.
|
58
|
+
def configuration_contexts
|
59
|
+
configuration.contexts
|
60
|
+
end
|
61
|
+
|
62
|
+
def merge_source(params)
|
63
|
+
source = configuration.source
|
64
|
+
source ? { source: source }.merge(params) : params
|
65
|
+
end
|
66
|
+
|
67
|
+
def current_context
|
68
|
+
configuration_contexts.inject({}) do |result, c|
|
69
|
+
current = c.respond_to?(:call) ? c.call.to_h : c.clone
|
70
|
+
result.merge(current)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def format_value(value)
|
75
|
+
configuration.value_formatter.call(value)
|
76
|
+
end
|
77
|
+
|
78
|
+
def format_key(key)
|
79
|
+
configuration.key_formatter.call(key)
|
80
|
+
end
|
81
|
+
|
82
|
+
def format_keys(params)
|
83
|
+
params.inject({}) do |normalized, (key, value)|
|
84
|
+
normalized.tap { |n| n[format_key(key)] = value }
|
85
|
+
end
|
33
86
|
end
|
34
87
|
|
35
88
|
def write(params)
|
36
|
-
tokens = params.map do |key, value|
|
37
|
-
key
|
38
|
-
next key if value == true
|
39
|
-
value = configuration.value_formatter.call(value)
|
40
|
-
[ key, value ] * ?=
|
89
|
+
tokens = format_keys(params).map do |key, value|
|
90
|
+
value == true ? key : [ key, format_value(value) ] * ?=
|
41
91
|
end
|
42
92
|
|
43
93
|
tokens.sort! if configuration.sort?
|
44
94
|
|
45
|
-
configuration.output.
|
95
|
+
configuration.output.print tokens.join(" ") + "\n"
|
96
|
+
end
|
97
|
+
|
98
|
+
def log_with_prefix(prefix, key, value)
|
99
|
+
log Hash["#{prefix}##{key}", value]
|
46
100
|
end
|
47
101
|
|
48
102
|
def wrap(params)
|
data/lib/l2meter/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: l2meter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pavel Pravosud
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-09-
|
11
|
+
date: 2015-09-15 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|