From 25dac92eeb1ffa83ec549bfed0b19672c5847d80 Mon Sep 17 00:00:00 2001
From: John Keiser <john@johnkeiser.com>
Date: Mon, 31 Aug 2015 14:38:50 -0700
Subject: Pass deprecations through formatter instead of logs

---
 lib/chef/application.rb                  |  2 +-
 lib/chef/chef_class.rb                   | 16 ++++++++++
 lib/chef/cookbook_version.rb             |  6 ++--
 lib/chef/deprecation/warnings.rb         |  5 ++-
 lib/chef/dsl/recipe.rb                   |  5 ++-
 lib/chef/dsl/resources.rb                |  4 +--
 lib/chef/event_dispatch/base.rb          | 15 ++++++---
 lib/chef/event_dispatch/dispatcher.rb    |  4 ++-
 lib/chef/formatters/base.rb              |  3 ++
 lib/chef/formatters/doc.rb               | 36 ++++++++++++++++++++++
 lib/chef/knife/core/subcommand_loader.rb |  6 ++--
 lib/chef/log.rb                          |  3 +-
 lib/chef/mixin/deprecation.rb            | 16 +++++-----
 lib/chef/node_map.rb                     |  4 +--
 lib/chef/property.rb                     |  2 +-
 lib/chef/provider.rb                     |  2 +-
 lib/chef/provider_resolver.rb            |  4 +--
 lib/chef/resource.rb                     |  6 ++--
 lib/chef/resource/chef_gem.rb            |  6 ++--
 lib/chef/resource/file/verification.rb   |  2 +-
 lib/chef/resource_resolver.rb            |  6 ++--
 lib/chef/run_context.rb                  |  8 ++---
 spec/integration/client/client_spec.rb   | 53 ++++++++++++++++++++++++++++++++
 spec/unit/property_spec.rb               | 28 +++++++++--------
 24 files changed, 180 insertions(+), 62 deletions(-)

diff --git a/lib/chef/application.rb b/lib/chef/application.rb
index 0563822ede..f43b6bbd8d 100644
--- a/lib/chef/application.rb
+++ b/lib/chef/application.rb
@@ -382,7 +382,7 @@ class Chef
 
     def emit_warnings
       if Chef::Config[:chef_gem_compile_time]
-        Chef::Log.deprecation "setting chef_gem_compile_time to true is deprecated"
+        Chef.log.deprecation "setting chef_gem_compile_time to true is deprecated"
       end
     end
 
diff --git a/lib/chef/chef_class.rb b/lib/chef/chef_class.rb
index 458ac82467..e825bedb34 100644
--- a/lib/chef/chef_class.rb
+++ b/lib/chef/chef_class.rb
@@ -190,6 +190,22 @@ class Chef
     def resource_handler_map
       @resource_handler_map ||= Chef::Platform::ResourceHandlerMap.instance
     end
+
+    #
+    # @overload log
+    # Get the current log object.
+    #
+    # @return An object that supports `deprecation(message)`
+    #
+    # @example
+    #     run_context.log.deprecation("Deprecated!")
+    #
+    # @api private
+    def log
+      # `run_context.events` is the primary deprecation target if we're in a run. If we
+      # are not yet in a run, print to `Chef::Log`.
+      (run_context && run_context.events) || Chef::Log
+    end
   end
 
   reset!
diff --git a/lib/chef/cookbook_version.rb b/lib/chef/cookbook_version.rb
index 8d302eeec2..0a7e1df2f7 100644
--- a/lib/chef/cookbook_version.rb
+++ b/lib/chef/cookbook_version.rb
@@ -51,12 +51,12 @@ class Chef
     attr_accessor :metadata_filenames
 
     def status=(new_status)
-      Chef::Log.deprecation("Deprecated method `status' called from #{caller(1).first}. This method will be removed")
+      Chef.log.deprecation("Deprecated method `status' called. This method will be removed.", caller(1..1))
       @status = new_status
     end
 
     def status
-      Chef::Log.deprecation("Deprecated method `status' called from #{caller(1).first}. This method will be removed")
+      Chef.log.deprecation("Deprecated method `status' called. This method will be removed.", caller(1..1))
       @status
     end
 
@@ -480,7 +480,7 @@ class Chef
     # @deprecated This method was used by the Ruby Chef Server and is no longer
     #   needed. There is no replacement.
     def generate_manifest_with_urls(&url_generator)
-      Chef::Log.deprecation("Deprecated method #generate_manifest_with_urls called from #{caller(1).first}")
+      Chef.log.deprecation("Deprecated method #generate_manifest_with_urls.", caller(1..1))
 
       rendered_manifest = manifest.dup
       COOKBOOK_SEGMENTS.each do |segment|
diff --git a/lib/chef/deprecation/warnings.rb b/lib/chef/deprecation/warnings.rb
index 34f468ff53..616f179d53 100644
--- a/lib/chef/deprecation/warnings.rb
+++ b/lib/chef/deprecation/warnings.rb
@@ -26,9 +26,8 @@ class Chef
           define_method(name) do |*args|
             message = []
             message << "Method '#{name}' of '#{self.class}' is deprecated. It will be removed in Chef 12."
-            message << "Please update your cookbooks accordingly. Accessed from:"
-            caller[0..3].each {|l| message << l}
-            Chef::Log.deprecation message
+            message << "Please update your cookbooks accordingly."
+            Chef.log.deprecation(message, caller(0..3))
             super(*args)
           end
         end
diff --git a/lib/chef/dsl/recipe.rb b/lib/chef/dsl/recipe.rb
index 29cfcd478c..e3b91a7eab 100644
--- a/lib/chef/dsl/recipe.rb
+++ b/lib/chef/dsl/recipe.rb
@@ -140,8 +140,7 @@ class Chef
         # method_missing manually. Not a fan. Not. A. Fan.
         #
         if respond_to?(method_symbol)
-          Chef::Log.deprecation("Calling method_missing(#{method_symbol.inspect}) directly is deprecated in Chef 12 and will be removed in Chef 13.")
-          Chef::Log.deprecation("Use public_send() or send() instead.")
+          Chef.log.deprecation("Calling method_missing(#{method_symbol.inspect}) directly is deprecated in Chef 12 and will be removed in Chef 13. Use public_send() or send() instead.")
           return send(method_symbol, *args, &block)
         end
 
@@ -150,7 +149,7 @@ class Chef
         # never called.  DEPRECATED.
         #
         if run_context.definitions.has_key?(method_symbol.to_sym)
-          Chef::Log.deprecation("Definition #{method_symbol} (#{run_context.definitions[method_symbol.to_sym]}) was added to the run_context without calling Chef::DSL::Definitions.add_definition(#{method_symbol.to_sym.inspect}).  This will become required in Chef 13.")
+          Chef.log.deprecation("Definition #{method_symbol} (#{run_context.definitions[method_symbol.to_sym]}) was added to the run_context without calling Chef::DSL::Definitions.add_definition(#{method_symbol.to_sym.inspect}).  This will become required in Chef 13.")
           Chef::DSL::Definitions.add_definition(method_symbol)
           return send(method_symbol, *args, &block)
         end
diff --git a/lib/chef/dsl/resources.rb b/lib/chef/dsl/resources.rb
index f15beaeab0..8e75223b0d 100644
--- a/lib/chef/dsl/resources.rb
+++ b/lib/chef/dsl/resources.rb
@@ -11,14 +11,14 @@ class Chef
         begin
           module_eval(<<-EOM, __FILE__, __LINE__+1)
             def #{dsl_name}(*args, &block)
-              Chef::Log.deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (\#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: \#{args}") if args.size > 1
+              Chef.log.deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (\#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: \#{args}") if args.size > 1
               declare_resource(#{dsl_name.inspect}, args[0], caller[0], &block)
             end
           EOM
         rescue SyntaxError
           # Handle the case where dsl_name has spaces, etc.
           define_method(dsl_name.to_sym) do |*args, &block|
-            Chef::Log.deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: #{args}") if args.size > 1
+            Chef.log.deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: #{args}") if args.size > 1
             declare_resource(dsl_name, args[0], caller[0], &block)
           end
         end
diff --git a/lib/chef/event_dispatch/base.rb b/lib/chef/event_dispatch/base.rb
index 0ae5101029..1c9a58be23 100644
--- a/lib/chef/event_dispatch/base.rb
+++ b/lib/chef/event_dispatch/base.rb
@@ -47,14 +47,19 @@ class Chef
       def ohai_completed(node)
       end
 
-      # Already have a client key, assuming this node has registered.
+      # Announce that we're not going to register the client. Generally because
+      # we already have the private key, or because we're deliberately not using
+      # a key.
       def skipping_registration(node_name, config)
       end
 
-      # About to attempt to register as +node_name+
+      # About to attempt to create a private key registered to the server with
+      # client +node_name+.
       def registration_start(node_name, config)
       end
 
+      # Successfully created the private key and registered this client with the
+      # server.
       def registration_completed
       end
 
@@ -340,7 +345,6 @@ class Chef
       def resource_completed(resource)
       end
 
-
       # A stream has opened.
       def stream_opened(stream, options = {})
       end
@@ -376,8 +380,9 @@ class Chef
       def whyrun_assumption(action, resource, message)
       end
 
-      ## TODO: deprecation warning. this way we can queue them up and present
-      #  them all at once.
+      # Emit a message about something being deprecated.
+      def deprecation(message, location=caller(2..2)[0])
+      end
 
       # An uncategorized message. This supports the case that a user needs to
       # pass output that doesn't fit into one of the callbacks above. Note that
diff --git a/lib/chef/event_dispatch/dispatcher.rb b/lib/chef/event_dispatch/dispatcher.rb
index 9e17d78507..0c5b27514c 100644
--- a/lib/chef/event_dispatch/dispatcher.rb
+++ b/lib/chef/event_dispatch/dispatcher.rb
@@ -28,6 +28,9 @@ class Chef
       # Define a method that will be forwarded to all
       def self.def_forwarding_method(method_name)
         define_method(method_name) do |*args|
+          if method_name == :deprecation && args.size == 1
+            args << caller(2..2)[0]
+          end
           @subscribers.each do |s|
             # Skip new/unsupported event names.
             if s.respond_to?(method_name)
@@ -49,4 +52,3 @@ class Chef
     end
   end
 end
-
diff --git a/lib/chef/formatters/base.rb b/lib/chef/formatters/base.rb
index c901068aa0..d3756ef00c 100644
--- a/lib/chef/formatters/base.rb
+++ b/lib/chef/formatters/base.rb
@@ -212,6 +212,9 @@ class Chef
         file_load_failed(path, exception)
       end
 
+      def deprecation(message, location=caller(2..2)[0])
+        Chef::Log.deprecation("#{message} at #{location}")
+      end
     end
 
 
diff --git a/lib/chef/formatters/doc.rb b/lib/chef/formatters/doc.rb
index a5d7e210c5..614cc44e6d 100644
--- a/lib/chef/formatters/doc.rb
+++ b/lib/chef/formatters/doc.rb
@@ -43,6 +43,26 @@ class Chef
 
       def run_completed(node)
         @end_time = Time.now
+        # Print out deprecations.
+        if !deprecations.empty?
+          puts_line ""
+          puts_line "Deprecated features used!"
+          deprecations.each do |message, locations|
+            if locations.size == 1
+              puts_line "  #{message} at #{locations.size} location:"
+            else
+              puts_line "  #{message} at #{locations.size} locations:"
+            end
+            locations.each do |location|
+              prefix = "    - "
+              Array(location).each do |line|
+                puts_line "#{prefix}#{line}"
+                prefix = "      "
+              end
+            end
+          end
+          puts_line ""
+        end
         if Chef::Config[:why_run]
           puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources would have been updated"
         else
@@ -336,6 +356,16 @@ class Chef
         end
       end
 
+      def deprecation(message, location=caller(2..2)[0])
+        if Chef::Config[:treat_deprecation_warnings_as_errors]
+          super
+        end
+
+        # Save deprecations to the screen until the end
+        deprecations[message] ||= Set.new
+        deprecations[message] << location
+      end
+
       def indent
         indent_by(2)
       end
@@ -343,6 +373,12 @@ class Chef
       def unindent
         indent_by(-2)
       end
+
+      protected
+
+      def deprecations
+        @deprecations ||= {}
+      end
     end
   end
 end
diff --git a/lib/chef/knife/core/subcommand_loader.rb b/lib/chef/knife/core/subcommand_loader.rb
index 1d359ffd53..646a75a21c 100644
--- a/lib/chef/knife/core/subcommand_loader.rb
+++ b/lib/chef/knife/core/subcommand_loader.rb
@@ -51,7 +51,7 @@ class Chef
           Chef::Log.debug("Using autogenerated hashed command manifest #{plugin_manifest_path}")
           Knife::SubcommandLoader::HashedCommandLoader.new(chef_config_dir, plugin_manifest)
         elsif custom_manifest?
-          Chef::Log.deprecation("Using custom manifest #{plugin_manifest_path} is deprecated.  Please use a `knife rehash` autogenerated manifest instead.")
+          Chef.log.deprecation("Using custom manifest #{plugin_manifest_path} is deprecated.  Please use a `knife rehash` autogenerated manifest instead.")
           Knife::SubcommandLoader::CustomManifestLoader.new(chef_config_dir, plugin_manifest)
         else
           Knife::SubcommandLoader::GemGlobLoader.new(chef_config_dir)
@@ -84,7 +84,7 @@ class Chef
         # Deprecated and un-used instance variable.
         @env = env
         unless env.nil?
-          Chef::Log.deprecation("The env argument to Chef::Knife::SubcommandLoader is deprecated. If you are using env to inject/mock HOME, consider mocking Chef::Util::PathHelper.home instead.")
+          Chef.log.deprecation("The env argument to Chef::Knife::SubcommandLoader is deprecated. If you are using env to inject/mock HOME, consider mocking Chef::Util::PathHelper.home instead.")
         end
       end
 
@@ -149,7 +149,7 @@ class Chef
       # to get in the past.
       #
       def subcommand_files
-        Chef::Log.deprecation "Using Chef::Knife::SubcommandLoader directly is deprecated.
+        Chef.log.deprecation "Using Chef::Knife::SubcommandLoader directly is deprecated.
 Please use Chef::Knife::SubcommandLoader.for_config(chef_config_dir, env)"
         @subcommand_files ||= if Chef::Knife::SubcommandLoader.plugin_manifest?
                                 Chef::Knife::SubcommandLoader::CustomManifestLoader.new(chef_config_dir, env).subcommand_files
diff --git a/lib/chef/log.rb b/lib/chef/log.rb
index 9b27778a40..2cf08324c8 100644
--- a/lib/chef/log.rb
+++ b/lib/chef/log.rb
@@ -37,7 +37,8 @@ class Chef
       end
     end
 
-    def self.deprecation(msg=nil, &block)
+    def self.deprecation(msg=nil, location=caller(2..2)[0], &block)
+      msg = Array(msg) + [ location ] if location
       if Chef::Config[:treat_deprecation_warnings_as_errors]
         error(msg, &block)
         raise Chef::Exceptions::DeprecatedFeatureError.new(msg)
diff --git a/lib/chef/mixin/deprecation.rb b/lib/chef/mixin/deprecation.rb
index a3eacf75cb..c90daee4f5 100644
--- a/lib/chef/mixin/deprecation.rb
+++ b/lib/chef/mixin/deprecation.rb
@@ -102,20 +102,20 @@ class Chef
 
       def deprecated_attr_reader(name, alternative, level=:warn)
         define_method(name) do
-          Chef::Log.deprecation("#{self.class}.#{name} is deprecated. Support will be removed in a future release.")
-          Chef::Log.deprecation(alternative)
-          Chef::Log.deprecation("Called from:")
-          caller[0..3].each {|c| Chef::Log.deprecation(c)}
+          Chef.log.deprecation("#{self.class}.#{name} is deprecated. Support will be removed in a future release.")
+          Chef.log.deprecation(alternative)
+          Chef.log.deprecation("Called from:")
+          caller[0..3].each {|c| Chef.log.deprecation(c)}
           instance_variable_get("@#{name}")
         end
       end
 
       def deprecated_attr_writer(name, alternative, level=:warn)
         define_method("#{name}=") do |value|
-          Chef::Log.deprecation("Writing to #{self.class}.#{name} with #{name}= is deprecated. Support will be removed in a future release.")
-          Chef::Log.deprecation(alternative)
-          Chef::Log.deprecation("Called from:")
-          caller[0..3].each {|c| Chef::Log.deprecation(c)}
+          Chef.log.deprecation("Writing to #{self.class}.#{name} with #{name}= is deprecated. Support will be removed in a future release.")
+          Chef.log.deprecation(alternative)
+          Chef.log.deprecation("Called from:")
+          caller[0..3].each {|c| Chef.log.deprecation(c)}
           instance_variable_set("@#{name}", value)
         end
       end
diff --git a/lib/chef/node_map.rb b/lib/chef/node_map.rb
index d905c8779e..2e62054b80 100644
--- a/lib/chef/node_map.rb
+++ b/lib/chef/node_map.rb
@@ -32,8 +32,8 @@ class Chef
     # @return [NodeMap] Returns self for possible chaining
     #
     def set(key, value, platform: nil, platform_version: nil, platform_family: nil, os: nil, on_platform: nil, on_platforms: nil, canonical: nil, override: nil, &block)
-      Chef::Log.deprecation "The on_platform option to node_map has been deprecated" if on_platform
-      Chef::Log.deprecation "The on_platforms option to node_map has been deprecated" if on_platforms
+      Chef.log.deprecation("The on_platform option to node_map has been deprecated") if on_platform
+      Chef.log.deprecation("The on_platforms option to node_map has been deprecated") if on_platforms
       platform ||= on_platform || on_platforms
       filters = {}
       filters[:platform] = platform if platform
diff --git a/lib/chef/property.rb b/lib/chef/property.rb
index 1a3b8ec72c..ccdc711698 100644
--- a/lib/chef/property.rb
+++ b/lib/chef/property.rb
@@ -228,7 +228,7 @@ class Chef
       if value.nil? && !explicitly_accepts_nil?(resource)
         # If you say "my_property nil" and the property explicitly accepts
         # nil values, we consider this a get.
-        Chef::Log.deprecation("#{name} nil currently does not overwrite the value of #{name}. This will change in Chef 13, and the value will be set to nil instead. Please change your code to explicitly accept nil using \"property :#{name}, [MyType, nil]\", or stop setting this value to nil.")
+        Chef.log.deprecation("#{name} nil currently does not overwrite the value of #{name}. This will change in Chef 13, and the value will be set to nil instead. Please change your code to explicitly accept nil using \"property :#{name}, [MyType, nil]\", or stop setting this value to nil.")
         return get(resource)
       end
 
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
index f2a493c3e6..5f2430e26e 100644
--- a/lib/chef/provider.rb
+++ b/lib/chef/provider.rb
@@ -421,7 +421,7 @@ class Chef
     module DeprecatedLWRPClass
       def const_missing(class_name)
         if deprecated_constants[class_name.to_sym]
-          Chef::Log.deprecation("Using an LWRP provider by its name (#{class_name}) directly is no longer supported in Chef 12 and will be removed.  Use Chef::ProviderResolver.new(node, resource, action) instead.")
+          Chef.log.deprecation("Using an LWRP provider by its name (#{class_name}) directly is no longer supported in Chef 12 and will be removed.  Use Chef::ProviderResolver.new(node, resource, action) instead.")
           deprecated_constants[class_name.to_sym]
         else
           raise NameError, "uninitialized constant Chef::Provider::#{class_name}"
diff --git a/lib/chef/provider_resolver.rb b/lib/chef/provider_resolver.rb
index 8459bc1328..113b1081ee 100644
--- a/lib/chef/provider_resolver.rb
+++ b/lib/chef/provider_resolver.rb
@@ -157,8 +157,8 @@ class Chef
             # perf concern otherwise.)
             handlers = providers.select { |handler| overrode_provides?(handler) && handler.provides?(node, resource) }
             handlers.each do |handler|
-              Chef::Log.deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource.resource_name}, but provides #{resource.resource_name.inspect} was never called!")
-              Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
+              Chef.log.deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource.resource_name}, but provides #{resource.resource_name.inspect} was never called!")
+              Chef.log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
             end
           end
           handlers
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index 5bef40625f..6b8c5434f5 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -809,7 +809,7 @@ class Chef
       end
 
       if !options[:default].frozen? && (options[:default].is_a?(Array) || options[:default].is_a?(Hash))
-        Chef::Log.warn("Property #{self}.#{name} has an array or hash default (#{options[:default]}). This means that if one resource modifies or appends to it, all other resources of the same type will also see the changes. Either freeze the constant with `.freeze` to prevent appending, or use lazy { #{options[:default].inspect} }.")
+        Chef.log.deprecation("Property #{self}.#{name} has an array or hash default (#{options[:default]}). This means that if one resource modifies or appends to it, all other resources of the same type will also see the changes. Either freeze the constant with `.freeze` to prevent appending, or use lazy { #{options[:default].inspect} }.")
       end
 
       local_properties = properties(false)
@@ -1211,7 +1211,7 @@ class Chef
     # @deprecated Use resource_name instead.
     #
     def self.dsl_name
-      Chef::Log.deprecation "Resource.dsl_name is deprecated and will be removed in Chef 13.  Use resource_name instead."
+      Chef.log.deprecation "Resource.dsl_name is deprecated and will be removed in Chef 13.  Use resource_name instead."
       if name
         name = self.name.split('::')[-1]
         convert_to_snake_case(name)
@@ -1288,7 +1288,7 @@ class Chef
     #
     def self.provider_base(arg=nil)
       if arg
-        Chef::Log.deprecation("Resource.provider_base is deprecated and will be removed in Chef 13. Use provides on the provider, or provider on the resource, instead.")
+        Chef.log.deprecation("Resource.provider_base is deprecated and will be removed in Chef 13. Use provides on the provider, or provider on the resource, instead.")
       end
       @provider_base ||= arg || Chef::Provider
     end
diff --git a/lib/chef/resource/chef_gem.rb b/lib/chef/resource/chef_gem.rb
index 0c2fdfa819..4d198421ce 100644
--- a/lib/chef/resource/chef_gem.rb
+++ b/lib/chef/resource/chef_gem.rb
@@ -50,9 +50,9 @@ class Chef
         # Chef::Resource.run_action: Caveat: this skips Chef::Runner.run_action, where notifications are handled
         # Action could be an array of symbols, but probably won't (think install + enable for a package)
         if compile_time.nil?
-          Chef::Log.deprecation "#{self} chef_gem compile_time installation is deprecated"
-          Chef::Log.deprecation "#{self} Please set `compile_time false` on the resource to use the new behavior."
-          Chef::Log.deprecation "#{self} or set `compile_time true` on the resource if compile_time behavior is required."
+          Chef.log.deprecation "#{self} chef_gem compile_time installation is deprecated"
+          Chef.log.deprecation "#{self} Please set `compile_time false` on the resource to use the new behavior."
+          Chef.log.deprecation "#{self} or set `compile_time true` on the resource if compile_time behavior is required."
         end
 
         if compile_time || compile_time.nil?
diff --git a/lib/chef/resource/file/verification.rb b/lib/chef/resource/file/verification.rb
index faf4791884..654f2d72ce 100644
--- a/lib/chef/resource/file/verification.rb
+++ b/lib/chef/resource/file/verification.rb
@@ -108,7 +108,7 @@ class Chef
         def verify_command(path, opts)
           # First implementation interpolated `file`; docs & RFC claim `path`
           # is interpolated. Until `file` can be deprecated, interpolate both.
-          Chef::Log.deprecation(
+          Chef.log.deprecation(
             '%{file} is deprecated in verify command and will not be '\
             'supported in Chef 13. Please use %{path} instead.'
           ) if @command.include?('%{file}')
diff --git a/lib/chef/resource_resolver.rb b/lib/chef/resource_resolver.rb
index 47b3df18af..14b0ff849d 100644
--- a/lib/chef/resource_resolver.rb
+++ b/lib/chef/resource_resolver.rb
@@ -56,7 +56,7 @@ class Chef
     attr_reader :resource_name
     # @api private
     def resource
-      Chef::Log.deprecation("Chef::ResourceResolver.resource deprecated.  Use resource_name instead.")
+      Chef.log.deprecation("Chef::ResourceResolver.resource deprecated.  Use resource_name instead.")
       resource_name
     end
     # @api private
@@ -174,8 +174,8 @@ class Chef
         if handlers.empty?
           handlers = resources.select { |handler| overrode_provides?(handler) && handler.provides?(node, resource_name) }
           handlers.each do |handler|
-            Chef::Log.deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource_name}, but provides #{resource_name.inspect} was never called!")
-            Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
+            Chef.log.deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource_name}, but provides #{resource_name.inspect} was never called!")
+            Chef.log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
           end
         end
         handlers
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index b1113f594e..dd2da0d7ac 100644
--- a/lib/chef/run_context.rb
+++ b/lib/chef/run_context.rb
@@ -533,22 +533,22 @@ ERROR_MESSAGE
       # These need to be settable so deploy can run a resource_collection
       # independent of any cookbooks via +recipe_eval+
       def resource_collection=(value)
-        Chef::Log.deprecation("Setting run_context.resource_collection will be removed in a future Chef.  Use run_context.create_child to create a new RunContext instead.")
+        Chef.log.deprecation("Setting run_context.resource_collection will be removed in a future Chef.  Use run_context.create_child to create a new RunContext instead.")
         @resource_collection = value
       end
 
       def audits=(value)
-        Chef::Log.deprecation("Setting run_context.audits will be removed in a future Chef.  Use run_context.create_child to create a new RunContext instead.")
+        Chef.log.deprecation("Setting run_context.audits will be removed in a future Chef.  Use run_context.create_child to create a new RunContext instead.")
         @audits = value
       end
 
       def immediate_notification_collection=(value)
-        Chef::Log.deprecation("Setting run_context.immediate_notification_collection will be removed in a future Chef.  Use run_context.create_child to create a new RunContext instead.")
+        Chef.log.deprecation("Setting run_context.immediate_notification_collection will be removed in a future Chef.  Use run_context.create_child to create a new RunContext instead.")
         @immediate_notification_collection = value
       end
 
       def delayed_notification_collection=(value)
-        Chef::Log.deprecation("Setting run_context.delayed_notification_collection will be removed in a future Chef.  Use run_context.create_child to create a new RunContext instead.")
+        Chef.log.deprecation("Setting run_context.delayed_notification_collection will be removed in a future Chef.  Use run_context.create_child to create a new RunContext instead.")
         @delayed_notification_collection = value
       end
     end
diff --git a/spec/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb
index 8c72048965..1a030c130b 100644
--- a/spec/integration/client/client_spec.rb
+++ b/spec/integration/client/client_spec.rb
@@ -303,6 +303,59 @@ EOM
 
   end
 
+  when_the_repository "has a cookbook that generates deprecation warnings" do
+    before do
+      file 'cookbooks/x/recipes/default.rb', <<-EOM
+        class ::MyResource < Chef::Resource
+          use_automatic_resource_name
+          property :x, default: []
+          property :y, default: {}
+        end
+
+        my_resource 'blah' do
+          1.upto(10) do
+            x nil
+          end
+          x nil
+        end
+      EOM
+    end
+
+    def match_indices(regex, str)
+      result = []
+      pos = 0
+      while match = regex.match(str, pos)
+        result << match.begin(0)
+        pos = match.end(0) + 1
+      end
+      result
+    end
+
+    it "should output each deprecation warning only once, at the end of the run" do
+      file 'config/client.rb', <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+# Mimick what happens when you are on the console
+formatters << :doc
+log_level :warn
+EOM
+
+      ENV.delete('CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS')
+
+      result = shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+      expect(result.error?).to be_falsey
+
+      # Search to the end of the client run in the output
+      run_complete = result.stdout.index("Running handlers complete")
+      expect(run_complete).to be >= 0
+
+      # Make sure there is exactly one result for each, and that it occurs *after* the complete message.
+      expect(match_indices(/MyResource.x has an array or hash default/, result.stdout)).to match([ be > run_complete ])
+      expect(match_indices(/MyResource.y has an array or hash default/, result.stdout)).to match([ be > run_complete ])
+      expect(match_indices(/nil currently does not overwrite the value of/, result.stdout)).to match([ be > run_complete ])
+    end
+  end
+
   when_the_repository "has a cookbook with only an audit recipe" do
 
     before do
diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb
index 55eaead9ba..a9b592ec46 100644
--- a/spec/unit/property_spec.rb
+++ b/spec/unit/property_spec.rb
@@ -462,22 +462,26 @@ describe "Chef::Resource.property" do
     end
 
     context "hash default" do
-      with_property ':x, default: {}' do
-        it "when x is not set, it returns {}" do
-          expect(resource.x).to eq({})
-        end
-        it "The same exact value is returned multiple times in a row" do
-          value = resource.x
-          expect(value).to eq({})
-          expect(resource.x.object_id).to eq(value.object_id)
-        end
-        it "Multiple instances of x receive the exact same value" do
-          expect(resource.x.object_id).to eq(resource_class.new('blah2').x.object_id)
+      context "(deprecations allowed)" do
+        before { Chef::Config[:treat_deprecation_warnings_as_errors] = false }
+
+        with_property ':x, default: {}' do
+          it "when x is not set, it returns {}" do
+            expect(resource.x).to eq({})
+          end
+          it "The same exact value is returned multiple times in a row" do
+            value = resource.x
+            expect(value).to eq({})
+            expect(resource.x.object_id).to eq(value.object_id)
+          end
+          it "Multiple instances of x receive the exact same value" do
+            expect(resource.x.object_id).to eq(resource_class.new('blah2').x.object_id)
+          end
         end
       end
 
       it "when a property is declared with default: {}, a warning is issued" do
-        expect(Chef::Log).to receive(:warn).with(match(/^Property .+\.x has an array or hash default \(\{\}\)\. This means that if one resource modifies or appends to it, all other resources of the same type will also see the changes\. Either freeze the constant with \`\.freeze\` to prevent appending, or use lazy \{ \{\} \}\.$/))
+        expect(Chef::Log).to receive(:deprecation).with(match(/^Property .+\.x has an array or hash default \(\{\}\)\. This means that if one resource modifies or appends to it, all other resources of the same type will also see the changes\. Either freeze the constant with \`\.freeze\` to prevent appending, or use lazy \{ \{\} \}\.$/))
         resource_class.class_eval("property :x, default: {}", __FILE__, __LINE__)
         expect(resource.x).to eq({})
       end
-- 
cgit v1.2.1