-
Notifications
You must be signed in to change notification settings - Fork 7
feat(logging): GoogleSdkLoggerDelegator initial implementation #49
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| # Copyright 2025 Google LLC | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # https://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| require "delegate" | ||
|
|
||
| module Google | ||
| module Logging | ||
| # A delegator that wraps a logger or its duck-type instance to add dynamic | ||
| # filtering based on GOOGLE_SDK_RUBY_LOGGING_GEMS environment variable, and | ||
| # error supression based on suppress_logger_errors parameter | ||
| class GoogleSdkLoggerDelegator < SimpleDelegator | ||
| # @private | ||
| # The environment variable that controls which gems have logging enabled. | ||
| ENV_VAR = "GOOGLE_SDK_RUBY_LOGGING_GEMS".freeze | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "LOGGING_GEMS_ENV_VAR" |
||
|
|
||
| # @private | ||
| # The full list of methods a compatible logger must implement. | ||
| REQUIRED_LOGGER_METHODS = [ | ||
| :level, :level=, :sev_threshold, :sev_threshold=, | ||
| :progname, :progname=, :formatter, :formatter=, | ||
| :datetime_format, :datetime_format=, | ||
| :debug?, :info?, :warn?, :error?, :fatal?, | ||
| :debug!, :info!, :warn!, :error!, :fatal!, | ||
| :close, :reopen, | ||
| :<<, :add, :debug, :info, :warn, :error, :fatal, :unknown | ||
| ].freeze | ||
|
|
||
| private_constant :ENV_VAR, :REQUIRED_LOGGER_METHODS | ||
|
|
||
| # @param gem_name [String] The name of the gem this logger is for. | ||
| # @param logger [Logger] The custom logger instance to wrap. | ||
| # @param suppress_logger_errors [Boolean] Whether to swallow exceptions in the wrapped logger. | ||
| def initialize gem_name, logger, suppress_logger_errors: true | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should make it same as |
||
| unless is_logger_type? logger | ||
| raise ArgumentError, "A valid, compatible logger instance is required." | ||
| end | ||
|
|
||
| super logger | ||
| @gem_name = gem_name | ||
| @suppress_logger_errors = suppress_logger_errors | ||
| end | ||
|
|
||
| # @!group Logging Methods with Filtering | ||
|
|
||
| # Override each logging method to insert the filter check before | ||
| # delegating to the corresponding method on the wrapped logger. | ||
|
|
||
| def << msg | ||
| return true unless logging_enabled? | ||
| suppress_errors { super } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. something like |
||
| end | ||
|
|
||
| def add severity, message = nil, progname = nil | ||
| return true unless logging_enabled? | ||
| suppress_errors { super } | ||
| end | ||
|
|
||
| def debug message = nil, &block | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since it's Ruby 3.1 we can probably drop the block variable name and just use |
||
| return true unless logging_enabled? | ||
| suppress_errors { super } | ||
| end | ||
|
|
||
| def info message = nil, &block | ||
| return true unless logging_enabled? | ||
| suppress_errors { super } | ||
| end | ||
|
|
||
| def warn message = nil, &block | ||
| return true unless logging_enabled? | ||
| suppress_errors { super } | ||
| end | ||
|
|
||
| def error message = nil, &block | ||
| return true unless logging_enabled? | ||
| suppress_errors { super } | ||
| end | ||
|
|
||
| def fatal message = nil, &block | ||
| return true unless logging_enabled? | ||
| suppress_errors { super } | ||
| end | ||
|
|
||
| def unknown message = nil, &block | ||
| return true unless logging_enabled? | ||
| suppress_errors { super } | ||
| end | ||
| # @!endgroup | ||
|
|
||
| private | ||
|
|
||
| def suppress_errors | ||
| yield | ||
| rescue StandardError | ||
| raise unless @suppress_logger_errors | ||
| true | ||
| end | ||
|
|
||
| def is_logger_type? logger | ||
| return false if logger.nil? | ||
| REQUIRED_LOGGER_METHODS.all? { |m| logger.respond_to? m } | ||
| end | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks like a class-level (static) method |
||
|
|
||
| # Checks if logging is enabled for the current gem. | ||
| def logging_enabled? | ||
| current_env_var = ENV[ENV_VAR] | ||
|
|
||
| # Return the cached result if the ENV var hasn't changed. | ||
| if @cached_env_var_value == current_env_var | ||
| return @cached_logging_enabled_result | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| end | ||
|
|
||
| # If the ENV var has changed (or on the first run), re-evaluate. | ||
| new_result = false | ||
| begin | ||
| if current_env_var == "none" | ||
| new_result = false | ||
| elsif current_env_var == "all" | ||
| new_result = true | ||
| else | ||
| packages = current_env_var&.gsub(/\s+/, "")&.split(",") || [] | ||
| new_result = packages.include? @gem_name | ||
| end | ||
| rescue StandardError => e | ||
| Kernel.warn "Failed to determine logging configuration for #{@gem_name}. " \ | ||
| "Logging disabled. Error: #{e.class}: #{e.message}" | ||
| new_result = false | ||
| end | ||
|
|
||
| # Update the cache with the new values. | ||
| @cached_env_var_value = current_env_var | ||
| @cached_logging_enabled_result = new_result | ||
|
|
||
| new_result | ||
| end | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,8 @@ | |
| require "google/logging/message" | ||
| require "google/logging/source_location" | ||
| require "google/logging/structured_formatter" | ||
| require "google/logging/google_sdk_filtered_logger" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ? |
||
| require "google/logging/google_sdk_logger_delegator" | ||
|
|
||
| ## | ||
| # Google products namespace | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider calling it "Proxy" or "Wrapper" instead of "Delegator".