timber 2.1.0.rc3 → 2.1.0.rc4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|