timber 2.5.1 → 2.6.0.pre.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -1
  3. data/lib/timber/config.rb +2 -1
  4. data/lib/timber/contexts/custom.rb +10 -3
  5. data/lib/timber/contexts/http.rb +23 -7
  6. data/lib/timber/contexts/organization.rb +14 -3
  7. data/lib/timber/contexts/release.rb +17 -4
  8. data/lib/timber/contexts/runtime.rb +28 -9
  9. data/lib/timber/contexts/session.rb +11 -2
  10. data/lib/timber/contexts/system.rb +13 -3
  11. data/lib/timber/contexts/user.rb +22 -6
  12. data/lib/timber/events/controller_call.rb +14 -24
  13. data/lib/timber/events/custom.rb +8 -5
  14. data/lib/timber/events/error.rb +11 -31
  15. data/lib/timber/events/http_request.rb +39 -13
  16. data/lib/timber/events/http_response.rb +32 -14
  17. data/lib/timber/events/sql_query.rb +10 -7
  18. data/lib/timber/events/template_render.rb +11 -5
  19. data/lib/timber/log_entry.rb +7 -31
  20. data/lib/timber/logger.rb +1 -5
  21. data/lib/timber/util.rb +2 -2
  22. data/lib/timber/util/attribute_normalizer.rb +90 -0
  23. data/lib/timber/util/hash.rb +51 -1
  24. data/lib/timber/util/non_nil_hash_builder.rb +38 -0
  25. data/lib/timber/version.rb +1 -1
  26. data/spec/timber/events/error_spec.rb +8 -22
  27. data/spec/timber/events/http_request_spec.rb +1 -1
  28. data/spec/timber/events_spec.rb +1 -1
  29. data/spec/timber/integrations/action_dispatch/debug_exceptions_spec.rb +1 -1
  30. data/spec/timber/log_entry_spec.rb +0 -39
  31. data/spec/timber/logger_spec.rb +0 -5
  32. data/spec/timber/util/attribute_normalizer_spec.rb +90 -0
  33. metadata +8 -8
  34. data/lib/timber/util/http_event.rb +0 -69
  35. data/lib/timber/util/object.rb +0 -15
  36. data/spec/timber/util/http_event_spec.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a87f39b30172987e5484e2cc7a7b8141df304872
4
- data.tar.gz: cbc461a99678fd533074bf6b8e9fdf91d4003bab
3
+ metadata.gz: 10838667c7f285fea9c51270f12594b2106299e1
4
+ data.tar.gz: f91eaaac75c0ed243686771c415a3f44d6c431b3
5
5
  SHA512:
6
- metadata.gz: d5af9920a09c610cc4fcdfb22c38db8b99e25f1b4232ed3c70350ec63965facef2ccd3f580e4b72cb08a8c795bcf8038a5ddc3ce22ea1c26968dc54f2453fe20
7
- data.tar.gz: 693d57eba475629b40c9bbbf639ebc6f87af2298eea376b9cc881a76ea224eeb75b0b9d08377a3ebde2d05854ce1d1c006c32679072cd5a721b7c80fc41e1848
6
+ metadata.gz: 8cc093229929a57566e15f4fe81a9f2fbf36c0259a85123e43cbe077e9a14828147620b60be1a7953560ac691923146dcccb2075adbe64fb331bae9a1e388206
7
+ data.tar.gz: 5478c747ded3aa62d3fcf0b877e5e09bccafb94c72e1b4812b2dea0cac2aa5fe2e86435532c66f16b35596178c64d2502a40687fe97437f13e6db641cbb296b8
data/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.6.0-beta1] - 2017-10-28
11
+
12
+ ### Fixed
13
+
14
+ - Encoding and rewind issues for file upload parameters have been resolved. Timber
15
+ improved attribute normalization across all contexts and events, ignoring binary
16
+ values like this in general.
17
+
10
18
  ## [2.5.1] - 2017-10-27
11
19
 
12
20
  ### Fixed
@@ -103,7 +111,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
103
111
  instead of applying back pressure.
104
112
 
105
113
 
106
- [Unreleased]: https://github.com/timberio/timber-ruby/compare/v2.5.1...HEAD
114
+ [Unreleased]: https://github.com/timberio/timber-ruby/compare/v2.6.0-beta1...HEAD
115
+ [2.6.0-beta1]: https://github.com/timberio/timber-ruby/compare/v2.5.1...v2.6.0-beta1
107
116
  [2.5.1]: https://github.com/timberio/timber-ruby/compare/v2.5.0...v2.5.1
108
117
  [2.5.0]: https://github.com/timberio/timber-ruby/compare/v2.4.0...v2.5.0
109
118
  [2.4.0]: https://github.com/timberio/timber-ruby/compare/v2.3.4...v2.4.0
data/lib/timber/config.rb CHANGED
@@ -25,6 +25,7 @@ module Timber
25
25
  end
26
26
  end
27
27
 
28
+ DEFAULT_HTTP_BODY_LIMIT = 2048.freeze
28
29
  DEVELOPMENT_NAME = "development".freeze
29
30
  PRODUCTION_NAME = "production".freeze
30
31
  STAGING_NAME = "staging".freeze
@@ -36,7 +37,7 @@ module Timber
36
37
 
37
38
  # @private
38
39
  def initialize
39
- @http_body_limit = 2048
40
+ @http_body_limit = DEFAULT_HTTP_BODY_LIMIT
40
41
  end
41
42
 
42
43
  # Convenience method for logging debug statements to the debug logger
@@ -24,13 +24,20 @@ module Timber
24
24
  attr_reader :type, :data
25
25
 
26
26
  def initialize(attributes)
27
- @type = Timber::Util::Object.try(attributes[:type], :to_sym) || raise(ArgumentError.new(":type is required"))
28
- @data = attributes[:data] || raise(ArgumentError.new(":data is required"))
27
+ normalizer = Util::AttributeNormalizer.new(attributes)
28
+ @type = normalizer.fetch!(:type, :symbol)
29
+ @data = normalizer.fetch!(:data, :hash)
29
30
  end
30
31
 
31
32
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
33
+ def to_hash
34
+ @to_hash ||= Util::NonNilHashBuilder.build do |h|
35
+ h.add(type, data)
36
+ end
37
+ end
38
+
32
39
  def as_json(options = {})
33
- {type => data}
40
+ to_hash
34
41
  end
35
42
  end
36
43
  end
@@ -13,22 +13,38 @@ module Timber
13
13
  # @note This context should be installed automatically through the,
14
14
  # {Intregrations::Rack::HTTPContext} Rack middleware.
15
15
  class HTTP < Context
16
+ HOST_MAX_BYTES = 256.freeze
17
+ METHOD_MAX_BYTES = 20.freeze
18
+ PATH_MAX_BYTES = 2048.freeze
19
+ REMOTE_ADDR_MAX_BYTES = 256.freeze
20
+ REQUEST_ID_MAX_BYTES = 256.freeze
21
+
16
22
  @keyspace = :http
17
23
 
18
24
  attr_reader :host, :method, :path, :remote_addr, :request_id
19
25
 
20
26
  def initialize(attributes)
21
- @host = attributes[:host]
22
- @method = attributes[:method] || raise(ArgumentError.new(":method is required"))
23
- @path = attributes[:path]
24
- @remote_addr = attributes[:remote_addr]
25
- @request_id = attributes[:request_id]
27
+ normalizer = Util::AttributeNormalizer.new(attributes)
28
+ @host = normalizer.fetch(:host, :string, :limit => HOST_MAX_BYTES)
29
+ @method = normalizer.fetch!(:method, :string, :upcase => true, :limit => METHOD_MAX_BYTES)
30
+ @path = normalizer.fetch(:path, :string, :limit => PATH_MAX_BYTES)
31
+ @remote_addr = normalizer.fetch(:remote_addr, :string, :limit => REMOTE_ADDR_MAX_BYTES)
32
+ @request_id = normalizer.fetch(:request_id, :string, :limit => REQUEST_ID_MAX_BYTES)
26
33
  end
27
34
 
28
35
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
36
+ def to_hash
37
+ @to_hash ||= Util::NonNilHashBuilder.build do |h|
38
+ h.add(:host, host)
39
+ h.add(:method, method)
40
+ h.add(:path, path)
41
+ h.add(:remote_addr, remote_addr)
42
+ h.add(:request_id, request_id)
43
+ end
44
+ end
45
+
29
46
  def as_json(_options = {})
30
- {:host => host, :method => method, :path => path, :remote_addr => remote_addr,
31
- :request_id => request_id}
47
+ to_hash
32
48
  end
33
49
  end
34
50
  end
@@ -19,18 +19,29 @@ module Timber
19
19
  # end
20
20
  #
21
21
  class Organization < Context
22
+ ID_MAX_BYTES = 256.freeze
23
+ NAME_MAX_BYTES = 256.freeze
24
+
22
25
  @keyspace = :organization
23
26
 
24
27
  attr_reader :id, :name
25
28
 
26
29
  def initialize(attributes)
27
- @id = Timber::Util::Object.try(attributes[:id], :to_s)
28
- @name = attributes[:name]
30
+ normalizer = Util::AttributeNormalizer.new(attributes)
31
+ @id = normalizer.fetch(:id, :string, :limit => ID_MAX_BYTES)
32
+ @name = normalizer.fetch(:name, :string, :limit => NAME_MAX_BYTES)
29
33
  end
30
34
 
31
35
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
36
+ def to_hash
37
+ @to_hash ||= Util::NonNilHashBuilder.build do |h|
38
+ h.add(:id, id)
39
+ h.add(:name, name)
40
+ end
41
+ end
42
+
32
43
  def as_json(_options = {})
33
- {id: id, name: name}
44
+ to_hash
34
45
  end
35
46
  end
36
47
  end
@@ -8,6 +8,10 @@ module Timber
8
8
  #
9
9
  # @note To automatically set this context, see {.from_env}.
10
10
  class Release < Context
11
+ COMMIT_HASH_MAX_BYTES = 256.freeze
12
+ CREATED_AT_MAX_BYTES = 256.freeze
13
+ VERSION_MAX_BYTES = 256.freeze
14
+
11
15
  @keyspace = :release
12
16
 
13
17
  class << self
@@ -36,14 +40,23 @@ module Timber
36
40
  attr_reader :commit_hash, :created_at, :version
37
41
 
38
42
  def initialize(attributes)
39
- @commit_hash = attributes[:commit_hash]
40
- @created_at = attributes[:created_at]
41
- @version = attributes[:version]
43
+ normalizer = Util::AttributeNormalizer.new(attributes)
44
+ @commit_hash = normalizer.fetch(:commit_hash, :string, :limit => COMMIT_HASH_MAX_BYTES)
45
+ @created_at = normalizer.fetch(:created_at, :string, :limit => CREATED_AT_MAX_BYTES)
46
+ @version = normalizer.fetch(:version, :string, :limit => VERSION_MAX_BYTES)
42
47
  end
43
48
 
44
49
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
50
+ def to_hash
51
+ @to_hash ||= Util::NonNilHashBuilder.build do |h|
52
+ h.add(:commit_hash, commit_hash)
53
+ h.add(:created_at, created_at)
54
+ h.add(:version, version)
55
+ end
56
+ end
57
+
45
58
  def as_json(_options = {})
46
- {commit_hash: commit_hash, created_at: created_at, version: version}
59
+ to_hash
47
60
  end
48
61
  end
49
62
  end
@@ -7,24 +7,43 @@ module Timber
7
7
  # origin in your code. For example, if you are debugging a specific class, you can narrow
8
8
  # by that class and see only it's logs.
9
9
  class Runtime < Context
10
+ APPLICATION_MAX_BYTES = 256.freeze
11
+ CLASS_NAME_MAX_BYTES = 256.freeze
12
+ FILE_MAX_BYTES = 1024.freeze
13
+ FUNCTION_MAX_BYTES = 256.freeze
14
+ MODULE_NAME_MAX_BYTES = 256.freeze
15
+ VM_PID_MAX_BYTES = 256.freeze
16
+
10
17
  @keyspace = :runtime
11
18
 
12
19
  attr_reader :application, :class_name, :file, :function, :line, :module_name, :vm_pid
13
20
 
14
21
  def initialize(attributes)
15
- @application = attributes[:application]
16
- @class_name = attributes[:class_name]
17
- @file = attributes[:file]
18
- @function = attributes[:function]
19
- @line = attributes[:line]
20
- @module_name = attributes[:module_name]
21
- @vm_pid = Timber::Util::Object.try(attributes[:vm_pid], :to_s)
22
+ normalizer = Util::AttributeNormalizer.new(attributes)
23
+ @application = normalizer.fetch(:application, :string, :limit => APPLICATION_MAX_BYTES)
24
+ @class_name = normalizer.fetch(:class_name, :string, :limit => CLASS_NAME_MAX_BYTES)
25
+ @file = normalizer.fetch(:file, :string, :limit => FILE_MAX_BYTES)
26
+ @function = normalizer.fetch(:function, :string, :limit => FUNCTION_MAX_BYTES)
27
+ @line = normalizer.fetch(:line, :integer)
28
+ @module_name = normalizer.fetch(:module_name, :string, :limit => MODULE_NAME_MAX_BYTES)
29
+ @vm_pid = normalizer.fetch(:vm_pid, :string, :limit => VM_PID_MAX_BYTES)
22
30
  end
23
31
 
24
32
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
33
+ def to_hash
34
+ @to_hash ||= Util::NonNilHashBuilder.build do |h|
35
+ h.add(:application, application)
36
+ h.add(:class_name, class_name)
37
+ h.add(:file, file)
38
+ h.add(:function, function)
39
+ h.add(:line, line)
40
+ h.add(:module_name, module_name)
41
+ h.add(:vm_pid, vm_pid)
42
+ end
43
+ end
44
+
25
45
  def as_json(_options = {})
26
- {application: application, class_name: class_name, file: file, function: function,
27
- line: line, module_name: module_name, vm_pid: vm_pid}
46
+ to_hash
28
47
  end
29
48
  end
30
49
  end
@@ -11,17 +11,26 @@ module Timber
11
11
  # @note This is tracked automatically with the {Integrations::Rack::SessionContext} rack
12
12
  # middleware.
13
13
  class Session < Context
14
+ ID_MAX_BYTES = 256.freeze
15
+
14
16
  @keyspace = :session
15
17
 
16
18
  attr_reader :id
17
19
 
18
20
  def initialize(attributes)
19
- @id = Timber::Util::Object.try(attributes[:id], :to_s) || raise(ArgumentError.new(":id is required"))
21
+ normalizer = Util::AttributeNormalizer.new(attributes)
22
+ @id = normalizer.fetch!(:id, :string, :limit => ID_MAX_BYTES)
20
23
  end
21
24
 
22
25
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
26
+ def to_hash
27
+ @to_hash ||= Util::NonNilHashBuilder.build do |h|
28
+ h.add(:id, id)
29
+ end
30
+ end
31
+
23
32
  def as_json(_options = {})
24
- {id: id}
33
+ to_hash
25
34
  end
26
35
  end
27
36
  end
@@ -8,18 +8,28 @@ module Timber
8
8
  # @note This is tracked automatically in {CurrentContext}. When the current context
9
9
  # is initialized, the system context gets added automatically.
10
10
  class System < Context
11
+ HOSTNAME_MAX_BYTES = 256.freeze
12
+
11
13
  @keyspace = :system
12
14
 
13
15
  attr_reader :hostname, :pid
14
16
 
15
17
  def initialize(attributes)
16
- @hostname = attributes[:hostname]
17
- @pid = Timber::Util::Object.try(attributes[:pid], :to_i)
18
+ normalizer = Util::AttributeNormalizer.new(attributes)
19
+ @hostname = normalizer.fetch(:hostname, :string, :limit => HOSTNAME_MAX_BYTES)
20
+ @pid = normalizer.fetch(:pid, :integer)
18
21
  end
19
22
 
20
23
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
24
+ def to_hash
25
+ @to_hash ||= Util::NonNilHashBuilder.build do |h|
26
+ h.add(:hostname, hostname)
27
+ h.add(:pid, pid)
28
+ end
29
+ end
30
+
21
31
  def as_json(_options = {})
22
- {hostname: hostname, pid: pid}
32
+ to_hash
23
33
  end
24
34
  end
25
35
  end
@@ -11,21 +11,37 @@ module Timber
11
11
  # middleware for supported authentication frameworks. See {Integrations::Rack::UserContext}
12
12
  # for more details.
13
13
  class User < Context
14
+ ID_MAX_BYTES = 256.freeze
15
+ NAME_MAX_BYTES = 256.freeze
16
+ EMAIL_MAX_BYTES = 256.freeze
17
+ TYPE_MAX_BYTES = 256.freeze
18
+
14
19
  @keyspace = :user
15
20
 
16
21
  attr_reader :id, :name, :email, :type, :meta
17
22
 
18
23
  def initialize(attributes)
19
- @id = Timber::Util::Object.try(attributes[:id], :to_s)
20
- @name = attributes[:name]
21
- @email = attributes[:email]
22
- @type = attributes[:type]
23
- @meta = attributes[:meta]
24
+ normalizer = Util::AttributeNormalizer.new(attributes)
25
+ @id = normalizer.fetch(:id, :string, :limit => ID_MAX_BYTES)
26
+ @name = normalizer.fetch(:name, :string, :limit => NAME_MAX_BYTES)
27
+ @email = normalizer.fetch(:email, :string, :limit => EMAIL_MAX_BYTES)
28
+ @type = normalizer.fetch(:type, :string, :limit => TYPE_MAX_BYTES)
29
+ @meta = normalizer.fetch(:meta, :hash)
24
30
  end
25
31
 
26
32
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
33
+ def to_hash
34
+ @to_hash ||= Util::NonNilHashBuilder.build do |h|
35
+ h.add(:id, id)
36
+ h.add(:name, name)
37
+ h.add(:email, email)
38
+ h.add(:type, type)
39
+ h.add(:meta, meta)
40
+ end
41
+ end
42
+
27
43
  def as_json(_options = {})
28
- {id: id, name: name, email: email, type: type, meta: meta}
44
+ to_hash
29
45
  end
30
46
  end
31
47
  end
@@ -8,22 +8,30 @@ module Timber
8
8
  # Processing by PagesController#home as HTML
9
9
  #
10
10
  # @note This event should be installed automatically through integrations,
11
- # such as the {Integrations::ActionController::LogSubscriber} integration.
11
+ # such as the {Integrations::ActionController} integration.
12
12
  class ControllerCall < Timber::Event
13
+ ACTION_MAX_BYTES = 256.freeze
14
+ FORMAT_MAX_BYTES = 256.freeze
15
+ CONTROLLER_MAX_BYTES = 256.freeze
13
16
  PARAMS_JSON_MAX_BYTES = 32_768.freeze
14
17
  PASSWORD_NAME = 'password'.freeze
15
18
 
16
19
  attr_reader :controller, :action, :params, :format
17
20
 
18
21
  def initialize(attributes)
19
- @controller = attributes[:controller] || raise(ArgumentError.new(":controller is required"))
20
- @action = attributes[:action] || raise(ArgumentError.new(":action is required"))
21
- @params = sanitize_params(attributes[:params])
22
- @format = attributes[:format]
22
+ normalizer = Util::AttributeNormalizer.new(attributes)
23
+ @controller = normalizer.fetch!(:controller, :string, :limit => CONTROLLER_MAX_BYTES)
24
+ @action = normalizer.fetch!(:action, :string, :limit => ACTION_MAX_BYTES)
25
+ @params = normalizer.fetch(:params, :hash, :sanitize => [PASSWORD_NAME])
26
+ @format = normalizer.fetch(:format, :string, :limit => FORMAT_MAX_BYTES)
23
27
  end
24
28
 
25
29
  def to_hash
26
- {controller: controller, action: action, params_json: params_json}
30
+ @to_hash ||= Util::NonNilHashBuilder.build do |h|
31
+ h.add(:controller, controller)
32
+ h.add(:action, action)
33
+ h.add(:params_json, params.to_json.byteslice(0, PARAMS_JSON_MAX_BYTES))
34
+ end
27
35
  end
28
36
  alias to_h to_hash
29
37
 
@@ -42,24 +50,6 @@ module Timber
42
50
  end
43
51
  message
44
52
  end
45
-
46
- private
47
- def params_json
48
- @params_json ||=
49
- if params.nil? || params == {}
50
- nil
51
- else
52
- params.to_json.byteslice(0, PARAMS_JSON_MAX_BYTES)
53
- end
54
- end
55
-
56
- def sanitize_params(params)
57
- if params.is_a?(::Hash)
58
- Util::Hash.sanitize(params, [PASSWORD_NAME])
59
- else
60
- params
61
- end
62
- end
63
53
  end
64
54
  end
65
55
  end
@@ -23,12 +23,13 @@ module Timber
23
23
  # @option attributes [Hash] :data A hash of JSON encodable data to be stored with the
24
24
  # log line.
25
25
  def initialize(attributes)
26
- @type = attributes[:type] || raise(ArgumentError.new(":type is required"))
27
- @message = attributes[:message] || raise(ArgumentError.new(":message is required"))
26
+ normalizer = Util::AttributeNormalizer.new(attributes)
27
+ @type = normalizer.fetch!(:type, :symbol)
28
+ @message = normalizer.fetch!(:message, :string)
28
29
 
29
- data = attributes[:data]
30
+ data = normalizer.fetch!(:data, :hash)
30
31
 
31
- if data.is_a?(Hash) && data[:time_ms].is_a?(Time)
32
+ if !data.nil? && data[:time_ms].is_a?(Time)
32
33
  data[:time_ms] = Timer.duration_ms(data[:time_ms])
33
34
  @message << " in #{data[:time_ms]}ms"
34
35
  end
@@ -37,7 +38,9 @@ module Timber
37
38
  end
38
39
 
39
40
  def to_hash
40
- {Timber::Util::Object.try(type, :to_sym) => data}
41
+ @to_hash ||= Util::NonNilHashBuilder.build do |h|
42
+ h.add(type, data)
43
+ end
41
44
  end
42
45
  alias to_h to_hash
43
46