Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add limits to #log_deprecation #3675

Merged
merged 1 commit into from
Jul 8, 2024
Merged

Conversation

delner
Copy link
Contributor

@delner delner commented May 30, 2024

What does this PR do?

This adds a limiting feature to the log_deprecation function. By default, there is no limit. However, if you provide a key:, then it will prevent any other message logged with the same key thereafter.

Motivation:

Some deprecated behavior may be on a frequently used path that could produce a lot of noise. Instead of logging every time, or never logging at all, make it possible to warn once.

Additional Notes:

I extracted the existing log_deprecation function into its own module (Core::Deprecations) and recomposed it back into the original Core module. Now that the deprecation behavior was becoming more sophisticated, I didn't want the core.rb file to scope creep; thus I isolated the behavior to its own file and module.

How to test the change?

Run the test suite.

@delner delner added core Involves Datadog core libraries dev/refactor Involves refactoring existing components deprecation Involves a deprecation labels May 30, 2024
@delner delner added this to the 2.0.0 milestone May 30, 2024
@delner delner self-assigned this May 30, 2024
@delner delner marked this pull request as ready for review May 31, 2024 14:11
@delner delner requested a review from a team as a code owner May 31, 2024 14:11
sig/datadog/core/deprecations.rbs Show resolved Hide resolved
lib/datadog/core/deprecations.rb Outdated Show resolved Hide resolved
lib/datadog/core/deprecations.rb Show resolved Hide resolved
sig/datadog/core/deprecations.rbs Outdated Show resolved Hide resolved
sig/datadog/core/deprecations.rbs Outdated Show resolved Hide resolved
sig/datadog/core/deprecations.rbs Outdated Show resolved Hide resolved
sig/datadog/core/deprecations.rbs Outdated Show resolved Hide resolved
module Datadog
module Core
module Deprecations
@_logged_deprecations: untyped
Copy link
Contributor

@lloeki lloeki Jun 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • From Hash.new(0) the expected hash values are Integer.
  • From _logged_deprecations[key] += 1 the hash values are indeed Integer and the keys are key.

To avoid repetition, let's call the key type "key" by defining a type alias.

Note: RBS type aliases are all lowercase, distinguishing them from Ruby types which are constants and thus start with an uppercase letter.

Note: the apparent key name clash is resolved at .rbs parse time, as there is no actual parsing ambiguity between type aliases and Ruby identifiers.

Also, from our discussion, the expected behaviour of key is to behave correctly as a hash key, which means it must respond to #hash and #eql?. Let's define an interface _Hashing for that.

Note: RBS interfaces start with an underscore followed by an uppercase letter. This isolates them from Ruby types, which follow constant conventions.

Note: _Hashing comes from the RBS doc.

Note: _*ing (_Hashing, _Reading, ...) is apparently a conventional way to name Ruby interfaces by "what they do", comparable to the *er convention in Go (e.g Stringer, Reader, Writer, ReadWriter, ReadWriteCloser, ...). I did not notice that and have been using a *able convention myself (_Stringable, _Readable, ...).

Note: since _Hashing might be used throughout we may ultimately want to hoist it in an upper level namespace and make more extensive and consistent use of it. Not in this PR though.

This gives us:

Suggested change
@_logged_deprecations: untyped
interface _Hashing
def hash: () -> ::Integer
def eql?: (untyped) -> bool
end
type key = _Hashing
@_logged_deprecations: ::Hash[key, ::Integer]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So IIUC, we don't do @logged_deprecations: ::Hash[::Object, ::Integer] because Object may not respond to the functions that make it "hashable", therefore, we define an interface to which objects used as Hash keys must conform to?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. In addition:

  • a BasicObject child instance may actually be hashable
  • an Object child instance may have their hash and eql? methods removed
  • it gets precise and explicit as to what is actually required, both in naming (Object is unclear as to what's the requirement is) and in which methods (through the definition of the interface)

Copy link
Contributor

@TonyCTHsu TonyCTHsu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see some changes to the method naming from this module. Currently, it is confusing because everything named with combination of log and deprecation and a bunch of !, ? _. Those knowledge should already been capture under the namespace of Deprecation.

@delner
Copy link
Contributor Author

delner commented Jun 4, 2024

@TonyCTHsu

  • everything named with combination of log and deprecation: this is because its a composable module: it could conflict with other method names in the class its composed into.
  • a bunch of !, ? _: I agree that I can strike the underscore in the one name. But the ? and ! are Ruby convention for those methods.

@delner
Copy link
Contributor Author

delner commented Jun 4, 2024

Thanks @lloeki for the detailed feedback on the RBS! I will try to apply that, and submit again for your consideration.

Copy link
Contributor

@p-datadog p-datadog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I am understanding this PR correctly, each class/module that extends Deprecations will get its own tracker of what was already warned about. I think this would be useful to include in the documentation for Deprecations.

@TonyCTHsu TonyCTHsu removed this from the 2.0.0 milestone Jun 5, 2024
@delner delner force-pushed the delner/add_log_deprecation_limits branch from c5456dd to 15cfb36 Compare June 18, 2024 18:33
@delner
Copy link
Contributor Author

delner commented Jun 18, 2024

@p-datadog Technically true, but its intention is to be composed into the Core module itself, so I don't really expect anyone to extend it.

@delner
Copy link
Contributor Author

delner commented Jun 18, 2024

@lloeki the guide and explanation about how to properly RBS type this was great. I definitely learned some things about RBS. Thank you! I think this is ready for another look.

nil
end
end
extend Core::Deprecations
Copy link
Contributor

@lloeki lloeki Jun 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed that one: probably this needs an update to sig/datadog/core.rbs with:

module Datadog
  module Core
  end

  extend Core::Configuration
  extend Tracing::Contrib::Extensions::Helpers
  extend Core::Deprecations
end

@@ -0,0 +1,22 @@
module Datadog
module Core
module Deprecations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

each class/module that extends Deprecations will get its own tracker of what was already warned about. I think this would be useful to include in the documentation for Deprecations.

I don't really expect anyone to extend it.

It might be possible to enforce this via a self type constraint:

Module declaration takes optional self type parameter, which defines a constraint about a class when the module is mixed.

Something like (not quite sure):

Suggested change
module Deprecations
module Deprecations : Core

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this with the change above causes:

sig/datadog/core.rbs:5:2: [error] Module self type constraint in type `::Datadog` doesn't satisfy: `singleton(::Datadog) <: ::Datadog::Core`
│ Diagnostic ID: RBS::ModuleSelfTypeError
│
└   extend Core::Deprecations
    ~~~~~~~~~~~~~~~~~~~~~~~~~

@delner delner force-pushed the delner/add_log_deprecation_limits branch from 15cfb36 to 365e344 Compare June 21, 2024 19:25
@delner delner force-pushed the delner/add_log_deprecation_limits branch from 365e344 to 41c614f Compare July 8, 2024 16:45

Datadog.logger.warn do
message = yield
message += ' This will be enforced in the next major release.' if disallowed_next_major
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static Analysis Violation (ruby-best-practices/concat-strings)

⚪ Notice - Best Practices - Link to Results

Avoid appending to string using +=

Suggested change
message += ' This will be enforced in the next major release.' if disallowed_next_major
message << ' This will be enforced in the next major release.' if disallowed_next_major

Leave feedback in #static-analysis

@delner delner merged commit d1e5618 into master Jul 8, 2024
170 checks passed
@delner delner deleted the delner/add_log_deprecation_limits branch July 8, 2024 20:16
@github-actions github-actions bot added this to the 2.2.0 milestone Jul 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Involves Datadog core libraries deprecation Involves a deprecation dev/refactor Involves refactoring existing components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants