timber 2.1.0.rc3 → 2.1.0.rc4
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 +24 -17
- data/lib/timber/cli/api.rb +6 -3
- data/lib/timber/cli/api/application.rb +7 -2
- data/lib/timber/cli/config_file.rb +14 -17
- data/lib/timber/cli/file_helper.rb +21 -9
- data/lib/timber/cli/installer.rb +3 -1
- data/lib/timber/cli/installers.rb +47 -39
- data/lib/timber/cli/installers/config_file.rb +60 -0
- data/lib/timber/cli/installers/other.rb +11 -7
- data/lib/timber/cli/installers/rails.rb +93 -146
- data/lib/timber/cli/installers/root.rb +43 -27
- data/lib/timber/cli/io.rb +15 -3
- data/lib/timber/cli/io/messages.rb +3 -4
- data/lib/timber/current_context.rb +15 -3
- data/lib/timber/log_entry.rb +0 -10
- data/lib/timber/logger.rb +7 -3
- data/lib/timber/util.rb +0 -1
- data/lib/timber/util/http_event.rb +6 -18
- data/lib/timber/util/request.rb +32 -11
- data/lib/timber/version.rb +1 -1
- data/spec/timber/cli/config_file_spec.rb +23 -0
- data/spec/timber/cli/installers/config_file_spec.rb +58 -0
- data/spec/timber/cli/installers/other_spec.rb +50 -0
- data/spec/timber/cli/installers/rails_spec.rb +290 -95
- data/spec/timber/cli/installers/root_spec.rb +8 -7
- data/spec/timber/current_context_spec.rb +15 -15
- data/spec/timber/events/http_server_request_spec.rb +1 -1
- data/spec/timber/log_devices/http_spec.rb +2 -2
- data/spec/timber/logger_spec.rb +5 -5
- data/spec/timber/util/http_event_spec.rb +2 -2
- metadata +9 -3
- data/lib/timber/util/string.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c4de508448203aa02a9dba6eec487ed04d0ffcf
|
4
|
+
data.tar.gz: 8c7e69aad8ab879841ca7d31910e09e86e5c7658
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e91f93d4548d7459c5ecdda73917a1cf188301feaab95de3bcd319e8b779a9f250507046010688c459bbfc1a06de058df9caf3329e834d0f6e49d31fe9099c22
|
7
|
+
data.tar.gz: d944798f07d4b1bb0ee0d07b34ce321b75918aa5c5b23107ca6e6f62ba532837d474f2f625092f513c977fe7362fbfd4831506383945e28419c68c8fe44950a1
|
data/README.md
CHANGED
@@ -26,8 +26,9 @@ focusing on logging.
|
|
26
26
|
3. **Seamlessly integrates with popular libraries and frameworks.** - Rails, Rack, Devise,
|
27
27
|
Omniauth, etc. [Automatically captures user context, HTTP context, and event data.](#third-party-integrations)
|
28
28
|
|
29
|
-
4. **Pairs with a modern console.** - Designed specifically for
|
30
|
-
usable,
|
29
|
+
4. **Pairs with a modern structured-logging console.** - Designed specifically for structured data,
|
30
|
+
hosted, instantly usable, tail users, trace requests.
|
31
|
+
[Checkout the docs](https://timber.io/docs/app/tutorials/).
|
31
32
|
|
32
33
|
|
33
34
|
## Installation
|
@@ -45,16 +46,16 @@ focusing on logging.
|
|
45
46
|
|
46
47
|
## How it works
|
47
48
|
|
48
|
-
Let's start with an example. Timber turns this:
|
49
|
+
Let's start with an example. Timber turns this production log line:
|
49
50
|
|
50
51
|
```
|
51
|
-
Sent 200 in 45.2ms
|
52
|
+
I, [2017-06-04T18:04:53.653812 #42348] INFO -- : [my.host.com] [df88dbaa-50fd-4178-85d7-d66279ea33b6] [192.32.23.12] [bfa8242cd9733bf0211e334be203f0d0] Sent 200 in 45.2ms
|
52
53
|
```
|
53
54
|
|
54
|
-
Into a
|
55
|
+
Into a structured [`http_server_response` event](https://timber.io/docs/ruby/events-and-context/http-server-response-event/).
|
55
56
|
|
56
57
|
```
|
57
|
-
Sent 200 in 45.2ms @metadata {"dt": "2017-02-02T01:33:21.154345Z", "level": "info", "context": {"http": {"method": "GET", "path": "/path", "remote_addr": "192.32.23.12", "request_id": "
|
58
|
+
Sent 200 in 45.2ms @metadata {"dt": "2017-02-02T01:33:21.154345Z", "level": "info", "context": {"http": {"method": "GET", "path": "/path", "remote_addr": "192.32.23.12", "request_id": "df88dbaa-50fd-4178-85d7-d66279ea33b6"}, "session": {"id": "bfa8242cd9733bf0211e334be203f0d0"}, "system": {"hostname": "my.host.com", "pid": "254354"}, "user": {"id": 1, "name": "Ben Johnson", "email": "bens@email.com"}}, "event": {"http_server_response": {"status": 200, "time_ms": 45.2}}}
|
58
59
|
```
|
59
60
|
|
60
61
|
Notice that instead of completely replacing your log messages,
|
@@ -72,7 +73,7 @@ logger.info("Sent 200 in 45.2ms")
|
|
72
73
|
|
73
74
|
Here's a better look at the metadata:
|
74
75
|
|
75
|
-
```
|
76
|
+
```js
|
76
77
|
{
|
77
78
|
"dt": "2017-02-02T01:33:21.154345Z",
|
78
79
|
"level": "info",
|
@@ -83,11 +84,14 @@ Here's a better look at the metadata:
|
|
83
84
|
"remote_addr": "192.32.23.12",
|
84
85
|
"request_id": "abcd1234"
|
85
86
|
},
|
87
|
+
"session": {
|
88
|
+
"id": "bfa8242cd9733bf0211e334be203f0d0"
|
89
|
+
},
|
86
90
|
"system": {
|
87
91
|
"hostname": "1.server.com",
|
88
92
|
"pid": "254354"
|
89
93
|
},
|
90
|
-
"user": {
|
94
|
+
"user": { // user identifiable logs :O
|
91
95
|
"id": 1,
|
92
96
|
"name": "Ben Johnson",
|
93
97
|
"email": "bens@email.com"
|
@@ -418,15 +422,18 @@ Lastly, you can checkout how we capture these events in
|
|
418
422
|
|
419
423
|
<details><summary><strong>Won't this increase the size of my log data?</strong></summary><p>
|
420
424
|
|
421
|
-
Yes
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
425
|
+
Yes, but it's no different than adding any other useful data to your logs, such as
|
426
|
+
[tags](http://api.rubyonrails.org/classes/ActiveSupport/TaggedLogging.html). A few
|
427
|
+
of things to note:
|
428
|
+
|
429
|
+
1. Timber generally _reduces_ the amount of logs your app generates, trading quality for quantity.
|
430
|
+
It does so by providing options to consolidate request / response logs, template logs, and
|
431
|
+
even silence logs that are not of value to you. (see [configuration](#configuration) for examples).
|
432
|
+
2. Timber lets you pick exactly which events and contexts you want.
|
433
|
+
(see [configuration](#configuration) for examples)
|
434
|
+
3. Your logging provider should be compressing your data and charging you accordingly. Log data
|
435
|
+
is notoriously repetitive, and the context Timber generates is repetitive.
|
436
|
+
Because of compression we've seen somes apps only incur a ~15% increase in data size.
|
430
437
|
|
431
438
|
Finally, log what is useful to you. Quality over quantity certainly applies to logging.
|
432
439
|
|
data/lib/timber/cli/api.rb
CHANGED
@@ -64,7 +64,7 @@ module Timber
|
|
64
64
|
HAS_LOGS_PATH = "/installer/has_logs".freeze
|
65
65
|
USER_AGENT = "Timber Ruby/#{Timber::VERSION} (HTTP)".freeze
|
66
66
|
|
67
|
-
|
67
|
+
attr_accessor :api_key
|
68
68
|
|
69
69
|
def initialize(api_key)
|
70
70
|
@api_key = api_key
|
@@ -103,7 +103,7 @@ module Timber
|
|
103
103
|
when 0
|
104
104
|
event(:waiting_for_logs)
|
105
105
|
when 20
|
106
|
-
event(:
|
106
|
+
event(:excessive_log_waiting)
|
107
107
|
when 60
|
108
108
|
raise LogsNotReceivedError.new
|
109
109
|
end
|
@@ -142,7 +142,10 @@ module Timber
|
|
142
142
|
end
|
143
143
|
|
144
144
|
def issue!(req)
|
145
|
-
|
145
|
+
if api_key
|
146
|
+
req['Authorization'] = "Basic #{encoded_api_key}"
|
147
|
+
end
|
148
|
+
|
146
149
|
req['User-Agent'] = USER_AGENT
|
147
150
|
req['X-Installer-Session-Id'] = @session_id
|
148
151
|
res = http.start do |http|
|
@@ -2,7 +2,8 @@ module Timber
|
|
2
2
|
class CLI
|
3
3
|
class API
|
4
4
|
class Application
|
5
|
-
|
5
|
+
DEVELOPMENT_ENVIRONMENT = "development".freeze
|
6
|
+
TEST_ENVIRONMENT = "test".freeze
|
6
7
|
HEROKU = "heroku".freeze
|
7
8
|
|
8
9
|
attr_accessor :api_key, :environment, :framework_type, :heroku_drain_url,
|
@@ -18,7 +19,11 @@ module Timber
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def development?
|
21
|
-
environment ==
|
22
|
+
environment == DEVELOPMENT_ENVIRONMENT
|
23
|
+
end
|
24
|
+
|
25
|
+
def test?
|
26
|
+
environment == TEST_ENVIRONMENT
|
22
27
|
end
|
23
28
|
|
24
29
|
def heroku?
|
@@ -7,27 +7,24 @@ module Timber
|
|
7
7
|
|
8
8
|
def initialize(path)
|
9
9
|
@path = path
|
10
|
-
|
10
|
+
end
|
11
|
+
|
12
|
+
def create!
|
13
|
+
FileHelper.write(path, content)
|
14
|
+
end
|
15
|
+
|
16
|
+
def exists?
|
17
|
+
File.exists?(path)
|
11
18
|
end
|
12
19
|
|
13
20
|
def logrageify!
|
14
|
-
append("config.logrageify!")
|
21
|
+
append!("config.logrageify!")
|
15
22
|
end
|
16
23
|
|
17
24
|
private
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def append(code)
|
23
|
-
current_content = get_content
|
24
|
-
if !current_content.include?(code)
|
25
|
-
if current_content.include?(insert_hook)
|
26
|
-
new_content = current_content.gsub(insert_hook, "#{code}\n\n#{insert_hook}")
|
27
|
-
FileHelper.write(path, new_content)
|
28
|
-
else
|
29
|
-
FileHelper.append(path, new_content)
|
30
|
-
end
|
25
|
+
def append!(code)
|
26
|
+
if !content.include?(code)
|
27
|
+
content.gsub!(insert_hook, "#{code}\n\n#{insert_hook}")
|
31
28
|
end
|
32
29
|
|
33
30
|
true
|
@@ -39,8 +36,8 @@ module Timber
|
|
39
36
|
|
40
37
|
# We provide this as an instance method so that the string is only defined when needed.
|
41
38
|
# This avoids allocating this string during normal app runtime.
|
42
|
-
def
|
43
|
-
<<-CONTENT
|
39
|
+
def content
|
40
|
+
@content ||= <<-CONTENT
|
44
41
|
# Timber.io Ruby Configuration - Simple Structured Logging
|
45
42
|
#
|
46
43
|
# ^ ^ ^ ^ ___I_ ^ ^ ^ ^ ^ ^ ^
|
@@ -1,32 +1,44 @@
|
|
1
1
|
module Timber
|
2
2
|
class CLI
|
3
|
-
|
4
|
-
|
3
|
+
class FileHelper
|
4
|
+
attr_reader :api
|
5
|
+
|
6
|
+
def initialize(api)
|
7
|
+
@api = api
|
8
|
+
end
|
9
|
+
|
10
|
+
def append(path, contents)
|
5
11
|
File.open(path, "a") do |f|
|
6
12
|
f.write(contents)
|
7
13
|
end
|
8
14
|
end
|
9
15
|
|
10
|
-
def
|
11
|
-
|
16
|
+
def exists?(path)
|
17
|
+
File.exists?(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_or_create(path, contents)
|
21
|
+
if !exists?(path)
|
12
22
|
write(path, contents)
|
13
23
|
end
|
14
24
|
|
15
|
-
|
25
|
+
read(path)
|
16
26
|
end
|
17
27
|
|
18
|
-
def
|
28
|
+
def read(path)
|
19
29
|
File.read(path)
|
20
30
|
end
|
21
31
|
|
22
|
-
def
|
32
|
+
def write(path, contents)
|
23
33
|
File.open(path, "w") do |f|
|
24
34
|
f.write(contents)
|
25
35
|
end
|
36
|
+
|
37
|
+
api.event(:file_written, path: path)
|
26
38
|
end
|
27
39
|
|
28
|
-
def
|
29
|
-
if !
|
40
|
+
def verify(path, io)
|
41
|
+
if !exists?(path)
|
30
42
|
io.puts ""
|
31
43
|
io.puts "Uh oh! It looks like we couldn't locate the #{path} file. "
|
32
44
|
io.puts "Please enter the correct path:"
|
data/lib/timber/cli/installer.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
+
require "timber/cli/file_helper"
|
1
2
|
require "timber/cli/io/messages"
|
2
3
|
|
3
4
|
module Timber
|
4
5
|
class CLI
|
5
6
|
class Installer
|
6
|
-
attr_reader :io, :api
|
7
|
+
attr_reader :io, :api, :file_helper
|
7
8
|
|
8
9
|
def initialize(io, api)
|
9
10
|
@io = io
|
10
11
|
@api = api
|
12
|
+
@file_helper = FileHelper.new(api)
|
11
13
|
end
|
12
14
|
|
13
15
|
def run(app)
|
@@ -9,57 +9,65 @@ module Timber
|
|
9
9
|
class CLI
|
10
10
|
module Installers
|
11
11
|
def self.run(api_key, io)
|
12
|
-
|
13
|
-
|
14
|
-
io.puts IO::Messages.contact, :green
|
15
|
-
io.puts IO::Messages.separator, :green
|
16
|
-
io.puts ""
|
12
|
+
api = API.new(api_key)
|
13
|
+
api.event(:started)
|
17
14
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
io.puts
|
22
|
-
io.puts
|
23
|
-
io.puts ""
|
24
|
-
io.puts "It takes less than a minute, with one click Google and Github registration."
|
15
|
+
begin
|
16
|
+
io.puts IO::Messages.header, :green
|
17
|
+
io.puts IO::Messages.separator, :green
|
18
|
+
io.puts IO::Messages.contact, :green
|
19
|
+
io.puts IO::Messages.separator, :green
|
25
20
|
io.puts ""
|
26
21
|
|
27
|
-
if
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
22
|
+
if !api_key
|
23
|
+
api.event(:no_api_key)
|
24
|
+
|
25
|
+
app_url = IO::Messages::APP_URL
|
26
|
+
|
27
|
+
io.puts "Hey there! Welcome to Timber. In order to proceed, you'll need an API key."
|
28
|
+
io.puts "You can grab one by registering at #{IO::ANSI.colorize(app_url, :blue)}."
|
29
|
+
io.puts ""
|
30
|
+
io.puts "It takes less than a minute, with one click Google and Github registration."
|
31
|
+
io.puts ""
|
32
|
+
|
33
|
+
if OSHelper.can_open?
|
34
|
+
case io.ask_yes_no("Open #{app_url}?")
|
35
|
+
when :yes
|
36
|
+
puts ""
|
37
|
+
io.task("Opening #{app_url}") do
|
38
|
+
OSHelper.open(app_url)
|
39
|
+
end
|
40
|
+
when :no
|
41
|
+
io.puts ""
|
42
|
+
io.puts "No problem, navigate to the following URL:"
|
43
|
+
io.puts ""
|
44
|
+
io.puts " #{IO::ANSI.colorize(app_url, :blue)}"
|
33
45
|
end
|
34
|
-
|
46
|
+
else
|
35
47
|
io.puts ""
|
36
|
-
io.puts "
|
48
|
+
io.puts "Please navigate to:"
|
37
49
|
io.puts ""
|
38
50
|
io.puts " #{IO::ANSI.colorize(app_url, :blue)}"
|
39
51
|
end
|
40
|
-
|
52
|
+
|
41
53
|
io.puts ""
|
42
|
-
io.puts "
|
54
|
+
io.puts "Once you obtain your API key, you can run the installer like"
|
43
55
|
io.puts ""
|
44
|
-
io.puts " #{IO::ANSI.colorize(
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
io.puts ""
|
52
|
-
io.puts "See you soon! 🎈"
|
53
|
-
io.puts ""
|
54
|
-
else
|
55
|
-
api = API.new(api_key)
|
56
|
-
api.event(:started)
|
57
|
-
|
58
|
-
io.api = api
|
56
|
+
io.puts " #{IO::ANSI.colorize("bundle exec timber my-api-key", :blue)}"
|
57
|
+
io.puts ""
|
58
|
+
io.puts "See you soon! 🎈"
|
59
|
+
io.puts ""
|
60
|
+
else
|
61
|
+
api.event(:api_key_provided)
|
62
|
+
io.api = api
|
59
63
|
|
60
|
-
|
64
|
+
app = api.application!
|
61
65
|
|
62
|
-
|
66
|
+
Root.new(io, api).run(app)
|
67
|
+
end
|
68
|
+
rescue Exception => e
|
69
|
+
api.event(:exception, message: e.message, stacktrace: e.backtrace)
|
70
|
+
raise e
|
63
71
|
end
|
64
72
|
end
|
65
73
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
begin
|
2
|
+
require "lograge"
|
3
|
+
rescue Exception
|
4
|
+
end
|
5
|
+
|
6
|
+
require "timber/cli/config_file"
|
7
|
+
require "timber/cli/installer"
|
8
|
+
require "timber/cli/io/messages"
|
9
|
+
|
10
|
+
module Timber
|
11
|
+
class CLI
|
12
|
+
module Installers
|
13
|
+
class ConfigFile < Installer
|
14
|
+
def run(app, path)
|
15
|
+
config_file = Timber::CLI::ConfigFile.new(path)
|
16
|
+
|
17
|
+
if config_file.exists?
|
18
|
+
io.puts ""
|
19
|
+
io.task_complete("#{config_file.path} already created")
|
20
|
+
return true
|
21
|
+
end
|
22
|
+
|
23
|
+
if logrageify?
|
24
|
+
config_file.logrageify!
|
25
|
+
end
|
26
|
+
|
27
|
+
io.puts ""
|
28
|
+
task_message = "Creating #{config_file.path}"
|
29
|
+
io.task(task_message) { config_file.create! }
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def logrageify?
|
34
|
+
if defined?(::Lograge)
|
35
|
+
io.puts ""
|
36
|
+
io.puts IO::Messages.separator
|
37
|
+
io.puts ""
|
38
|
+
io.puts "We noticed you have lograge installed. Would you like to configure "
|
39
|
+
io.puts "Timber to function similarly?"
|
40
|
+
io.puts "(This silences template renders, sql queries, and controller calls."
|
41
|
+
io.puts "You can always do this later in config/initialzers/timber.rb)"
|
42
|
+
io.puts ""
|
43
|
+
io.puts "y) Yes, configure Timber like lograge", :blue
|
44
|
+
io.puts "n) No, use the Rails logging defaults", :blue
|
45
|
+
io.puts ""
|
46
|
+
|
47
|
+
case io.ask_yes_no("Enter your choice:", event_prompt: "Logrageify?")
|
48
|
+
when :yes
|
49
|
+
true
|
50
|
+
when :no
|
51
|
+
false
|
52
|
+
end
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -14,13 +14,7 @@ module Timber
|
|
14
14
|
install_http(api_key_code)
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
io.puts IO::Messages.separator
|
19
|
-
io.puts ""
|
20
|
-
io.puts "We're going to send a few test messages to ensure communication is working."
|
21
|
-
io.puts ""
|
22
|
-
io.ask_to_proceed
|
23
|
-
io.puts ""
|
17
|
+
ask_to_proceed
|
24
18
|
end
|
25
19
|
|
26
20
|
private
|
@@ -48,6 +42,16 @@ module Timber
|
|
48
42
|
io.puts ""
|
49
43
|
io.ask_to_proceed
|
50
44
|
end
|
45
|
+
|
46
|
+
def ask_to_proceed
|
47
|
+
io.puts ""
|
48
|
+
io.puts IO::Messages.separator
|
49
|
+
io.puts ""
|
50
|
+
io.puts "We're going to send a few test messages to ensure communication is working."
|
51
|
+
io.puts ""
|
52
|
+
io.ask_to_proceed
|
53
|
+
io.puts ""
|
54
|
+
end
|
51
55
|
end
|
52
56
|
end
|
53
57
|
end
|