timber 2.0.24 → 2.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -2
  3. data/CHANGELOG +3 -0
  4. data/README.md +314 -59
  5. data/bin/timber +11 -2
  6. data/lib/timber.rb +2 -7
  7. data/lib/timber/cli.rb +16 -28
  8. data/lib/timber/cli/api.rb +80 -14
  9. data/lib/timber/cli/api/application.rb +30 -0
  10. data/lib/timber/cli/config_file.rb +66 -0
  11. data/lib/timber/cli/file_helper.rb +43 -0
  12. data/lib/timber/cli/installer.rb +58 -0
  13. data/lib/timber/cli/installers.rb +37 -0
  14. data/lib/timber/cli/installers/other.rb +47 -0
  15. data/lib/timber/cli/installers/rails.rb +255 -0
  16. data/lib/timber/cli/installers/root.rb +189 -0
  17. data/lib/timber/cli/io.rb +97 -0
  18. data/lib/timber/cli/io/ansi.rb +22 -0
  19. data/lib/timber/cli/io/messages.rb +213 -0
  20. data/lib/timber/cli/os_helper.rb +53 -0
  21. data/lib/timber/config.rb +97 -43
  22. data/lib/timber/config/integrations.rb +63 -0
  23. data/lib/timber/config/integrations/rack.rb +74 -0
  24. data/lib/timber/context.rb +13 -10
  25. data/lib/timber/contexts.rb +1 -0
  26. data/lib/timber/contexts/custom.rb +16 -3
  27. data/lib/timber/contexts/http.rb +10 -3
  28. data/lib/timber/contexts/organization.rb +4 -0
  29. data/lib/timber/contexts/release.rb +46 -0
  30. data/lib/timber/contexts/runtime.rb +7 -1
  31. data/lib/timber/contexts/session.rb +8 -1
  32. data/lib/timber/contexts/system.rb +5 -1
  33. data/lib/timber/contexts/user.rb +9 -2
  34. data/lib/timber/current_context.rb +43 -11
  35. data/lib/timber/events/controller_call.rb +4 -0
  36. data/lib/timber/events/custom.rb +13 -5
  37. data/lib/timber/events/exception.rb +4 -0
  38. data/lib/timber/events/http_client_request.rb +4 -0
  39. data/lib/timber/events/http_client_response.rb +4 -0
  40. data/lib/timber/events/http_server_request.rb +5 -0
  41. data/lib/timber/events/http_server_response.rb +15 -3
  42. data/lib/timber/events/sql_query.rb +3 -0
  43. data/lib/timber/events/template_render.rb +3 -0
  44. data/lib/timber/integration.rb +40 -0
  45. data/lib/timber/integrations.rb +21 -14
  46. data/lib/timber/integrations/action_controller.rb +18 -0
  47. data/lib/timber/integrations/action_controller/log_subscriber.rb +2 -0
  48. data/lib/timber/integrations/action_controller/log_subscriber/timber_log_subscriber.rb +6 -0
  49. data/lib/timber/integrations/action_dispatch.rb +23 -0
  50. data/lib/timber/integrations/action_dispatch/debug_exceptions.rb +2 -0
  51. data/lib/timber/integrations/action_view.rb +18 -0
  52. data/lib/timber/integrations/action_view/log_subscriber.rb +2 -0
  53. data/lib/timber/integrations/action_view/log_subscriber/timber_log_subscriber.rb +10 -0
  54. data/lib/timber/integrations/active_record.rb +18 -0
  55. data/lib/timber/integrations/active_record/log_subscriber.rb +2 -0
  56. data/lib/timber/integrations/active_record/log_subscriber/timber_log_subscriber.rb +8 -0
  57. data/lib/timber/integrations/rack.rb +12 -2
  58. data/lib/timber/integrations/rack/exception_event.rb +38 -5
  59. data/lib/timber/integrations/rack/http_context.rb +4 -6
  60. data/lib/timber/integrations/rack/http_events.rb +177 -27
  61. data/lib/timber/integrations/rack/middleware.rb +28 -0
  62. data/lib/timber/integrations/rack/session_context.rb +5 -6
  63. data/lib/timber/integrations/rack/user_context.rb +90 -43
  64. data/lib/timber/integrations/rails.rb +22 -0
  65. data/lib/timber/integrations/rails/rack_logger.rb +2 -0
  66. data/lib/timber/integrator.rb +18 -3
  67. data/lib/timber/log_devices/http.rb +107 -99
  68. data/lib/timber/log_devices/http/dropping_sized_queue.rb +26 -0
  69. data/lib/timber/log_devices/http/flushable_sized_queue.rb +42 -0
  70. data/lib/timber/log_entry.rb +14 -2
  71. data/lib/timber/logger.rb +51 -36
  72. data/lib/timber/overrides.rb +2 -0
  73. data/lib/timber/overrides/active_support_3_tagged_logging.rb +103 -0
  74. data/lib/timber/overrides/active_support_tagged_logging.rb +53 -90
  75. data/lib/timber/timer.rb +21 -0
  76. data/lib/timber/util/hash.rb +1 -1
  77. data/lib/timber/util/http_event.rb +16 -3
  78. data/lib/timber/version.rb +1 -1
  79. data/spec/support/timber.rb +2 -3
  80. data/spec/timber/cli/installers/rails_spec.rb +160 -0
  81. data/spec/timber/cli/installers/root_spec.rb +100 -0
  82. data/spec/timber/config_spec.rb +28 -0
  83. data/spec/timber/current_context_spec.rb +61 -12
  84. data/spec/timber/events/custom_spec.rb +13 -2
  85. data/spec/timber/events/exception_spec.rb +15 -0
  86. data/spec/timber/events/http_server_request_spec.rb +3 -3
  87. data/spec/timber/integrations/rack/http_events_spec.rb +101 -0
  88. data/spec/timber/log_devices/http_spec.rb +20 -4
  89. data/spec/timber/log_entry_spec.rb +2 -1
  90. data/spec/timber/logger_spec.rb +8 -8
  91. metadata +40 -9
  92. data/benchmarks/rails.rb +0 -122
  93. data/lib/timber/cli/application.rb +0 -28
  94. data/lib/timber/cli/install.rb +0 -196
  95. data/lib/timber/cli/io_helper.rb +0 -65
  96. data/lib/timber/cli/messages.rb +0 -180
  97. data/lib/timber/integrations/active_support/tagged_logging.rb +0 -71
@@ -0,0 +1,63 @@
1
+ require "timber/config/integrations/rack"
2
+ require "timber/integrations/action_controller"
3
+ require "timber/integrations/action_view"
4
+ require "timber/integrations/active_record"
5
+ require "timber/integrations/rack"
6
+
7
+ module Timber
8
+ class Config
9
+ # Convenience module for accessing the various `Timber::Integrations::*` classes
10
+ # through the {Timber::Config} object. Timber couples configuration with the class
11
+ # responsibls for implementing it. This provides for a tighter design, but also
12
+ # requires the user to understand and access the various classes. This module aims
13
+ # to provide a simple ruby-like configuration interface for internal Timber classes.
14
+ #
15
+ # For example:
16
+ #
17
+ # config = Timber::Config.instance
18
+ # config.integrations.active_record.silence = true
19
+ module Integrations
20
+ extend self
21
+
22
+ # Convenience method for accessing the {Timber::Integrations::ActionController} class
23
+ # specific configuration.
24
+ #
25
+ # @example
26
+ # config = Timber::Config.instance
27
+ # config.integrations.action_controller.silence = true
28
+ def action_controller
29
+ Timber::Integrations::ActionController
30
+ end
31
+
32
+ # Convenience method for accessing the {Timber::IIntegrations::ActionView} class
33
+ # specific configuration.
34
+ #
35
+ # @example
36
+ # config = Timber::Config.instance
37
+ # config.integrations.action_view.silence = true
38
+ def action_view
39
+ Timber::Integrations::ActionView
40
+ end
41
+
42
+ # Convenience method for accessing the {Timber::IIntegrations::ActiveRecord} class
43
+ # specific configuration.
44
+ #
45
+ # @example
46
+ # config = Timber::Config.instance
47
+ # config.integrations.active_record.silence = true
48
+ def active_record
49
+ Timber::Integrations::ActiveRecord
50
+ end
51
+
52
+ # Convenience method for accessing the various `Timber::IIntegrations::Rack::*`
53
+ # classes. See {Rack} for a list of methods available.
54
+ #
55
+ # @example
56
+ # config = Timber::Config.instance
57
+ # config.integrations.rack.http_events.enabled = true
58
+ def rack
59
+ Rack
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,74 @@
1
+ module Timber
2
+ class Config
3
+ module Integrations
4
+ # Convenience module for accessing the various `Timber::Integrations::Rack::*` classes
5
+ # through the {Timber::Config} object. Timber couples configuration with the class
6
+ # responsibls for implementing it. This provides for a tighter design, but also
7
+ # requires the user to understand and access the various classes. This module aims
8
+ # to provide a simple ruby-like configuration interface for internal Timber classes.
9
+ #
10
+ # For example:
11
+ #
12
+ # config = Timber::Config.instance
13
+ # config.integrations.rack.http_events.enabled = false
14
+ module Rack
15
+ extend self
16
+
17
+ # Convenience method for accessing the {Timber::Integrations::Rack::ExceptionEvent}
18
+ # middleware class specific configuration. See {Timber::Integrations::Rack::ExceptionEvent}
19
+ # for a list of methods available.
20
+ #
21
+ # @example
22
+ # config = Timber::Config.instance
23
+ # config.integrations.rack.exception_event.enabled = false
24
+ def exception_event
25
+ Timber::Integrations::Rack::ExceptionEvent
26
+ end
27
+
28
+ # Convenience method for accessing the {Timber::Integrations::Rack::HTTPContext}
29
+ # middleware class specific configuration. See {Timber::Integrations::Rack::HTTPContext}
30
+ # for a list of methods available.
31
+ #
32
+ # @example
33
+ # config = Timber::Config.instance
34
+ # config.integrations.rack.http_context.enabled = false
35
+ def http_context
36
+ Timber::Integrations::Rack::HTTPContext
37
+ end
38
+
39
+ # Convenience method for accessing the {Timber::Integrations::Rack::HTTPEvents}
40
+ # middleware class specific configuration. See {Timber::Integrations::Rack::HTTPEvents}
41
+ # for a list of methods available.
42
+ #
43
+ # @example
44
+ # config = Timber::Config.instance
45
+ # config.integrations.rack.http_events.enabled = false
46
+ def http_events
47
+ Timber::Integrations::Rack::HTTPEvents
48
+ end
49
+
50
+ # Convenience method for accessing the {Timber::Integrations::Rack::SessionContext}
51
+ # middleware class specific configuration. See {Timber::Integrations::Rack::SessionContext}
52
+ # for a list of methods available.
53
+ #
54
+ # @example
55
+ # config = Timber::Config.instance
56
+ # config.integrations.rack.session_context.enabled = false
57
+ def session_context
58
+ Timber::Integrations::Rack::SessionContext
59
+ end
60
+
61
+ # Convenience method for accessing the {Timber::Integrations::Rack::UserContext}
62
+ # middleware class specific configuration. See {Timber::Integrations::Rack::UserContext}
63
+ # for a list of methods available.
64
+ #
65
+ # @example
66
+ # config = Timber::Config.instance
67
+ # config.integrations.rack.user_context.enabled = false
68
+ def user_context
69
+ Timber::Integrations::Rack::UserContext
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -3,25 +3,28 @@ module Timber
3
3
  # @private
4
4
  class Context
5
5
  class << self
6
+ # The keyspace is the key used when storing the context.
7
+ # For example:
8
+ #
9
+ # {:build => {:version => "1.0.0"}}
10
+ #
11
+ # The keyspace in the above context is `:build`. This is required
12
+ # because it prevents key name conflicts. Without the keyspace
13
+ # it very possible another context type might also have a `:version`
14
+ # attribute.
6
15
  def keyspace
7
16
  @keyspace || raise(NotImplementedError.new)
8
17
  end
9
18
  end
10
19
 
11
- def keyspace
12
- self.class.keyspace
13
- end
14
-
20
+ # Returns a simple structure sufficient for encoding. We use
21
+ # `as_json` as the name since this is a ruby pattern.
15
22
  def as_json(options = {})
16
23
  raise NotImplementedError.new
17
24
  end
18
25
 
19
- def to_json(options = {})
20
- as_json.to_json(options)
21
- end
22
-
23
- def to_msgpack(*args)
24
- as_json.to_msgpack(*args)
26
+ def keyspace
27
+ self.class.keyspace
25
28
  end
26
29
  end
27
30
  end
@@ -1,6 +1,7 @@
1
1
  require "timber/contexts/custom"
2
2
  require "timber/contexts/http"
3
3
  require "timber/contexts/organization"
4
+ require "timber/contexts/release"
4
5
  require "timber/contexts/runtime"
5
6
  require "timber/contexts/session"
6
7
  require "timber/contexts/system"
@@ -1,10 +1,22 @@
1
+ require "timber/context"
2
+ require "timber/util"
3
+
1
4
  module Timber
2
5
  module Contexts
3
6
  # Custom contexts allow you to add application specific context not covered elsewhere.
7
+ # Any data added this way will be included in your logs. A good example is worker job
8
+ # IDs. When processing a job you might add the job ID to the context, allowing you to
9
+ # view *all* logs generated while processing that job, not just the logs that contain
10
+ # the ID.
11
+ #
12
+ # Note in the example below all custom contexts must contain a root key. This is to
13
+ # ensure attribute names and types never clash across your contexts. It gives you
14
+ # much cleaner pallete to organize your data on.
4
15
  #
5
- # @example Adding a context
16
+ # @example Adding a custom context
6
17
  # logger.with_context(build: {version: "1.0.0"}) do
7
- # # ... anything logged here will have the context ...
18
+ # # anything logged here will have the custom context above
19
+ # # when this block exits the context will no longer be included
8
20
  # end
9
21
  class Custom < Context
10
22
  @keyspace = :custom
@@ -16,7 +28,8 @@ module Timber
16
28
  @data = attributes[:data] || raise(ArgumentError.new(":data is required"))
17
29
  end
18
30
 
19
- def as_json(_options = {})
31
+ # Builds a hash representation of containing simply objects, suitable for serialization.
32
+ def as_json(options = {})
20
33
  {Timber::Util::Object.try(type, :to_sym) => data}
21
34
  end
22
35
  end
@@ -1,8 +1,14 @@
1
+ require "timber/context"
2
+
1
3
  module Timber
2
4
  module Contexts
3
- # The HTTP content tracks the current HTTP request being processed. This serves
4
- # as join data across your logs, allowing you to query all logs for any attribute
5
- # presented here. For example, viewing all logs for a given request_id.
5
+ # The HTTP context adds data about the current HTTP request being processed to your logs.
6
+ # This allows your to tail and filter by this data. A very useful piece of data this
7
+ # captures is the request ID. This gives you the ability to trace requests and view logs
8
+ # for a specific request only. For example, say you've searched your logs and found the
9
+ # specific line you are looking for, but it lacks context. With Timber you can simply
10
+ # click the request ID and "zoom out" to view all logs for that request. This gives you
11
+ # complete picture of how the log line in questio was generated.
6
12
  #
7
13
  # @note This context should be installed automatically through integrations,
8
14
  # such as the {Intregrations::Rack::HTTPContext} rack middleware.
@@ -18,6 +24,7 @@ module Timber
18
24
  @request_id = attributes[:request_id]
19
25
  end
20
26
 
27
+ # Builds a hash representation of containing simply objects, suitable for serialization.
21
28
  def as_json(_options = {})
22
29
  {:method => method, :path => path, :remote_addr => remote_addr, :request_id => request_id}
23
30
  end
@@ -1,3 +1,6 @@
1
+ require "timber/context"
2
+ require "timber/util"
3
+
1
4
  module Timber
2
5
  module Contexts
3
6
  # The organization context tracks the organization of the currently
@@ -25,6 +28,7 @@ module Timber
25
28
  @name = attributes[:name]
26
29
  end
27
30
 
31
+ # Builds a hash representation of containing simply objects, suitable for serialization.
28
32
  def as_json(_options = {})
29
33
  {id: Timber::Util::Object.try(id, :to_s), name: name}
30
34
  end
@@ -0,0 +1,46 @@
1
+ require "timber/context"
2
+ require "timber/util"
3
+
4
+ module Timber
5
+ module Contexts
6
+ # The release context tracks application releases / versions / deploys.
7
+ # To automatically set this context, see {.from_env}.
8
+ class Release < Context
9
+ @keyspace = :release
10
+
11
+ class << self
12
+ # Builds a release context based on environment variables. Simply add the
13
+ # `RELEASE_COMMIT`, `RELEASE_CREATED_AT`, or the `RELEASE_VERSION` env vars
14
+ # to get this context automatially. All are optional, but at least one
15
+ # must be present.
16
+ #
17
+ # If you're on Heroku, simply enable dyno metadata to get this automatically:
18
+ # https://devcenter.heroku.com/articles/dyno-metadata
19
+ def from_env
20
+ commit_hash = ENV['RELEASE_COMMIT'] || ENV['HEROKU_SLUG_COMMIT']
21
+ created_at = ENV['RELEASE_CREATED_AT'] || ENV['HEROKU_RELEASE_CREATED_AT']
22
+ version = ENV['RELEASE_VERSION'] || ENV['HEROKU_RELEASE_VERSION']
23
+
24
+ if commit_hash || created_at || version
25
+ new(commit_hash: commit_hash, created_at: created_at, version: version)
26
+ else
27
+ nil
28
+ end
29
+ end
30
+ end
31
+
32
+ attr_reader :commit_hash, :created_at, :version
33
+
34
+ def initialize(attributes)
35
+ @commit_hash = attributes[:commit_hash]
36
+ @created_at = attributes[:created_at]
37
+ @version = attributes[:version]
38
+ end
39
+
40
+ # Builds a hash representation of containing simply objects, suitable for serialization.
41
+ def as_json(_options = {})
42
+ {commit_hash: commit_hash, created_at: created_at, version: version}
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,6 +1,11 @@
1
+ require "timber/context"
2
+
1
3
  module Timber
2
4
  module Contexts
3
- # Tracks OS level process information, such as the process ID.
5
+ # The runtime context adds current runtime data to your logs, such as the file, line number,
6
+ # class or module name, etc. This makes it easy to tail and search your logs by their
7
+ # origin in your code. For example, if you are debugging a specific class, you can narrow
8
+ # by that class and see only it's logs.
4
9
  class Runtime < Context
5
10
  @keyspace = :runtime
6
11
 
@@ -15,6 +20,7 @@ module Timber
15
20
  @module_name = attributes[:module_name]
16
21
  end
17
22
 
23
+ # Builds a hash representation of containing simply objects, suitable for serialization.
18
24
  def as_json(_options = {})
19
25
  {application: application, class_name: class_name, file: file, function: function,
20
26
  line: line, module_name: module_name}
@@ -1,6 +1,12 @@
1
+ require "timber/context"
2
+ require "timber/util"
3
+
1
4
  module Timber
2
5
  module Contexts
3
- # The session context tracks the current session for the given user.
6
+ # The session context adds the current session ID to your logs. This allows your
7
+ # to tail and filter logs by specific session IDs. Moreover, it gives you a unique
8
+ # identifier to report on user activity by session. This way your logs can tell the
9
+ # story of how many time a user has engaged your site.
4
10
  #
5
11
  # @note This is tracked automatically with the {Integrations::Rack::SessionContext} rack
6
12
  # middleware.
@@ -13,6 +19,7 @@ module Timber
13
19
  @id = attributes[:id] || raise(ArgumentError.new(":id is required"))
14
20
  end
15
21
 
22
+ # Builds a hash representation of containing simply objects, suitable for serialization.
16
23
  def as_json(_options = {})
17
24
  {id: Timber::Util::Object.try(id, :to_s)}
18
25
  end
@@ -1,6 +1,9 @@
1
+ require "timber/context"
2
+ require "timber/util"
3
+
1
4
  module Timber
2
5
  module Contexts
3
- # Tracks OS level process information, such as the process ID.
6
+ # The system context tracks OS level process information, such as the process ID.
4
7
  class System < Context
5
8
  @keyspace = :system
6
9
 
@@ -12,6 +15,7 @@ module Timber
12
15
  @pid = @pid.to_s
13
16
  end
14
17
 
18
+ # Builds a hash representation of containing simply objects, suitable for serialization.
15
19
  def as_json(_options = {})
16
20
  {hostname: hostname, pid: Timber::Util::Object.try(pid, :to_s)}
17
21
  end
@@ -1,9 +1,15 @@
1
+ require "timber/context"
2
+ require "timber/util"
3
+
1
4
  module Timber
2
5
  module Contexts
3
- # The user context tracks the currently authenticated user.
6
+ # The user context adds data about the currently authenticated user to your logs.
7
+ # By adding this context all of your logs will contain user information. This allows
8
+ # filter and tail logs by specific users.
4
9
  #
5
10
  # @note This is tracked automatically with the {Integrations::Rack::UserContext} rack
6
- # middleware.
11
+ # middleware for supported authentication frameworks. See {Integrations::Rack::UserContext}
12
+ # for more details.
7
13
  class User < Context
8
14
  @keyspace = :user
9
15
 
@@ -15,6 +21,7 @@ module Timber
15
21
  @email = attributes[:email]
16
22
  end
17
23
 
24
+ # Builds a hash representation of containing simply objects, suitable for serialization.
18
25
  def as_json(_options = {})
19
26
  {id: Timber::Util::Object.try(id, :to_s), name: name, email: email}
20
27
  end
@@ -1,5 +1,7 @@
1
1
  require "singleton"
2
2
 
3
+ require "timber/contexts/release"
4
+
3
5
  module Timber
4
6
  # Holds the current context in a thread safe memory storage. This context is
5
7
  # appended to every log line. Think of context as join data between your log lines,
@@ -13,30 +15,43 @@ module Timber
13
15
  THREAD_NAMESPACE = :_timber_current_context.freeze
14
16
 
15
17
  class << self
16
- # Convenience method for {#with}. See {#with} for full details and examples.
18
+ # Convenience method for {CurrentContext#with}. See {CurrentContext#with} for more info.
17
19
  def with(*args, &block)
18
20
  instance.with(*args, &block)
19
21
  end
20
22
 
21
- # Convenience method for {#add}. See {#add} for full details and examples.
23
+ # Convenience method for {CurrentContext#add}. See {CurrentContext#add} for more info.
22
24
  def add(*args)
23
25
  instance.add(*args)
24
26
  end
25
27
 
26
- def hash(*args)
27
- instance.hash(*args)
28
+ # Convenience method for {CurrentContext#fetch}. See {CurrentContext#fetch} for more info.
29
+ def fetch(*args)
30
+ instance.fetch(*args)
28
31
  end
29
32
 
30
- # Convenience method for {#remove}. See {#remove} for full details and examples.
33
+ # Convenience method for {CurrentContext#remove}. See {CurrentContext#remove} for more info.
31
34
  def remove(*args)
32
35
  instance.remove(*args)
33
36
  end
34
37
 
38
+ # Convenience method for {CurrentContext#reset}. See {CurrentContext#reset} for more info.
35
39
  def reset(*args)
36
40
  instance.reset(*args)
37
41
  end
38
42
  end
39
43
 
44
+ # Instantiates a new object, automatically adding context based on env variables (if present).
45
+ # For example, the {Contexts::ReleaseContext} is automatically added if the proper environment
46
+ # variables are present. Please see that class for more details.
47
+ def initialize
48
+ super
49
+ release_context = Contexts::Release.from_env
50
+ if release_context
51
+ add(release_context)
52
+ end
53
+ end
54
+
40
55
  # Adds a context and then removes it when the block is finished executing.
41
56
  #
42
57
  # @note Because context is included with every log line, it is recommended that you limit this
@@ -85,12 +100,13 @@ module Timber
85
100
  hash[key] = json
86
101
  end
87
102
  end
103
+ expire_cache!
104
+ self
88
105
  end
89
106
 
90
- # The internal hash that is maintained. It is recommended that you use {#with} and {#add}
91
- # for hash maintenance.
92
- def hash
93
- Thread.current[THREAD_NAMESPACE] ||= {}
107
+ # Fetch a specific context by key.
108
+ def fetch(*args)
109
+ hash.fetch(*args)
94
110
  end
95
111
 
96
112
  # Removes a context. If you wish to remove by key, or some other way, use {#hash} and
@@ -113,18 +129,34 @@ module Timber
113
129
  end
114
130
  end
115
131
  end
132
+ expire_cache!
133
+ self
116
134
  end
117
135
 
118
136
  # Resets the context to be blank. Use this carefully! This will remove *any* context,
119
137
  # include context that is automatically included with Timber.
120
138
  def reset
121
139
  hash.clear
140
+ expire_cache!
141
+ self
122
142
  end
123
143
 
124
144
  # Snapshots the current context so that you get a moment in time representation of the context,
125
- # since the context can change as execution proceeds.
145
+ # since the context can change as execution proceeds. Note that individual contexts
146
+ # should be immutable, and we implement snapshot caching as a result of this assumption.
126
147
  def snapshot
127
- hash.clone
148
+ @snapshot ||= hash.clone
128
149
  end
150
+
151
+ private
152
+ # The internal hash that is maintained. Use {#with} and {#add} for hash maintenance.
153
+ def hash
154
+ Thread.current[THREAD_NAMESPACE] ||= {}
155
+ end
156
+
157
+ # Hook to clear any caching implement in this class
158
+ def expire_cache!
159
+ @snapshot = nil
160
+ end
129
161
  end
130
162
  end