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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0b8cfc00c1a6cbda74012122b4e767ae10e8721d
4
- data.tar.gz: 6f9e05a2828f438d5a7105bbca3cd78ce4b78fc9
3
+ metadata.gz: 865b41102fd7ebcd25b127d1e1bdd4d3a777fa05
4
+ data.tar.gz: cfd0071e73ec174fda966428c1f9421f8db1758f
5
5
  SHA512:
6
- metadata.gz: b067f592722b372bfd78ff272a72fa50adb5adf73a169db08d5084c98888e51d803f707dd73511f38ddc1c2f6e44997376d9b920719cd13c666ab3ce42a31762
7
- data.tar.gz: 222b0436a4c08bc6cd7f5dbfd58cb2ddb64f7ddca2b60d7f45eecfc8e8f3994ec6cffdb91bc4fee49d6096843f2efe7ab0ca5e60f703ac82a540e99f631ae962
6
+ metadata.gz: afebdeaec22a78f58b14b66156e0804a961c34f84b0cced488c0185d90acba204d9921bd904522a5a58720bc29ea6207432afd5ead81b3f758e20e609a083383
7
+ data.tar.gz: 1eb3117c0347549685dd6cb4b0ac4b85bf2d02656a533b928509c88467c6e9f8bb72ea736ec5d34ef09ef5a37d1eabafd3b7f21e3a5f77f9af63f4945877afd1
data/README.md CHANGED
@@ -1,35 +1,185 @@
1
1
  # L2meter
2
+ [![Gem Version](https://img.shields.io/gem/v/l2meter.svg)](https://rubygems.org/gems/l2meter)
3
+ [![Build Status](https://img.shields.io/travis/heroku/l2meter.svg)](http://travis-ci.org/heroku/l2meter)
4
+ [![Code Climate](https://img.shields.io/codeclimate/github/heroku/l2meter.svg)](https://codeclimate.com/github/heroku/l2meter)
2
5
 
3
- L2meter is a little gem that helps you build loggers that outputs things in l2met format.
6
+ L2meter is a little gem that helps you build loggers that outputs things in
7
+ l2met-friendly format.
4
8
 
5
- ### Usage
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
- Metrics = L2meter.build do |config|
9
- # sort output tokens, false by default
10
- config.sort = true
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
- # default context
13
- config.context = { name: "my-app-name" }
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
- # ... or dynamic context
16
- config.context do
17
- { random_thing: SecureRandom.uuid }
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
- # $stdout by default
21
- config.output = StringIO.new
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
- Metrics.log "Hello world" # => hello-world
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
- Metrics.log :foo, :bar, fizz: :buzz # => foo bar fizz=buzz
115
+ These are available configuration:
27
116
 
28
- Metrics.log :doing_work do # => doing-work at=start
29
- do_some_work #
30
- end # => doing-work at=finish elapsed=3.1234s
117
+ #### Global context
31
118
 
32
- Metrics.log :deez_nutz do # => deez-nutz at=start
33
- raise ArgumentError, "error here" #
34
- end # => deez-nutz at=exception exception=ArgumentError message="error here" elapsed=1.2345s
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
- @context_block = block
40
- end
41
-
42
- def context=(value)
43
- @static_context = value
45
+ @contexts = [block]
44
46
  end
45
47
 
46
- def get_context
47
- return @static_context if @static_context
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
@@ -6,10 +6,12 @@ module L2meter
6
6
  @configuration = configuration
7
7
  end
8
8
 
9
- def log(*args, **params)
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 = context.merge(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 context
32
- configuration.get_context
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 = configuration.key_formatter.call(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.puts tokens.join(" ")
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)
@@ -1,3 +1,3 @@
1
1
  module L2meter
2
- VERSION = "0.0.2".freeze
2
+ VERSION = "0.0.3".freeze
3
3
  end
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.2
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-10 00:00:00.000000000 Z
11
+ date: 2015-09-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: