appydays 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/appydays/loggable/sequel_logger.rb +2 -2
- data/lib/appydays/loggable/sidekiq_job_logger.rb +18 -4
- data/lib/appydays/loggable/spec_helpers.rb +1 -1
- data/lib/appydays/loggable.rb +73 -2
- data/lib/appydays/spec_helpers.rb +1 -1
- data/lib/appydays/version.rb +1 -1
- metadata +17 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: f7233dee300e207e49803a368896ad6b098641db88b703d54860002055609826
         | 
| 4 | 
            +
              data.tar.gz: 8c1d7694c1ab1695526676690900f1d5d12facee1adb4b3a823d409af22606a2
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: b45d24f4ae6769ea2bd3b42b2985c404339d6b74238c33ae15d3413d8b04088137442dc4a62710b9ed8fdc215bc1bc98f2f91c14c6370550034e5a24a2b992f5
         | 
| 7 | 
            +
              data.tar.gz: 27ac2dc1b1ee65373c807518df6a4cb0e2038b65f6ccababc20ba89a2b3cc3bd5a1042e06c838f69ebaa054f8c88ef6e4ce42e8e61a2be063aaad61a48989020
         | 
| @@ -45,7 +45,7 @@ class Sequel::Database | |
| 45 45 | 
             
                  :info,
         | 
| 46 46 | 
             
                  proc { args ? "#{message}; #{args.inspect}" : message },
         | 
| 47 47 | 
             
                  proc do
         | 
| 48 | 
            -
                    o = {message: | 
| 48 | 
            +
                    o = {message:}
         | 
| 49 49 | 
             
                    o[:args] = args unless args.nil?
         | 
| 50 50 | 
             
                    ["sequel_log", o]
         | 
| 51 51 | 
             
                  end,
         | 
| @@ -62,7 +62,7 @@ class Sequel::Database | |
| 62 62 | 
             
                  proc { "(#{'%0.6fs' % duration}) #{message}" },
         | 
| 63 63 | 
             
                  proc do
         | 
| 64 64 | 
             
                    query = AppydaysLogger.truncate_message(message)
         | 
| 65 | 
            -
                    params = {duration: duration * 1000, query: | 
| 65 | 
            +
                    params = {duration: duration * 1000, query:}
         | 
| 66 66 | 
             
                    if query != message
         | 
| 67 67 | 
             
                      params[:truncated] = true
         | 
| 68 68 | 
             
                      was_truncated = true
         | 
| @@ -20,7 +20,7 @@ class Appydays::Loggable::SidekiqJobLogger < Sidekiq::JobLogger | |
| 20 20 |  | 
| 21 21 | 
             
              Sidekiq.logger = self.logger
         | 
| 22 22 |  | 
| 23 | 
            -
              def call(item, _queue, & | 
| 23 | 
            +
              def call(item, _queue, &)
         | 
| 24 24 | 
             
                start = self.now
         | 
| 25 25 | 
             
                tags = {
         | 
| 26 26 | 
             
                  job_class: item["class"],
         | 
| @@ -28,7 +28,7 @@ class Appydays::Loggable::SidekiqJobLogger < Sidekiq::JobLogger | |
| 28 28 | 
             
                  thread_id: self.tid,
         | 
| 29 29 | 
             
                }
         | 
| 30 30 | 
             
                self.with_log_tags(tags) do
         | 
| 31 | 
            -
                  self.call_inner(start, & | 
| 31 | 
            +
                  self.call_inner(start, &)
         | 
| 32 32 | 
             
                end
         | 
| 33 33 | 
             
              end
         | 
| 34 34 |  | 
| @@ -43,11 +43,13 @@ class Appydays::Loggable::SidekiqJobLogger < Sidekiq::JobLogger | |
| 43 43 | 
             
                yield
         | 
| 44 44 | 
             
                duration = self.elapsed(start)
         | 
| 45 45 | 
             
                log_method = duration >= self.slow_job_seconds ? :warn : :info
         | 
| 46 | 
            -
                self.logger.send(log_method, "job_done", duration: duration * 1000)
         | 
| 46 | 
            +
                self.logger.send(log_method, "job_done", duration: duration * 1000, **self.class.job_tags)
         | 
| 47 47 | 
             
              rescue StandardError
         | 
| 48 48 | 
             
                # Do not log the error since it is probably a sidekiq retry error
         | 
| 49 | 
            -
                self.logger.error("job_fail", duration: self.elapsed(start) * 1000)
         | 
| 49 | 
            +
                self.logger.error("job_fail", duration: self.elapsed(start) * 1000, **self.class.job_tags)
         | 
| 50 50 | 
             
                raise
         | 
| 51 | 
            +
              ensure
         | 
| 52 | 
            +
                self.class.job_tags.clear
         | 
| 51 53 | 
             
              end
         | 
| 52 54 |  | 
| 53 55 | 
             
              protected def elapsed(start)
         | 
| @@ -58,6 +60,18 @@ class Appydays::Loggable::SidekiqJobLogger < Sidekiq::JobLogger | |
| 58 60 | 
             
                return ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
         | 
| 59 61 | 
             
              end
         | 
| 60 62 |  | 
| 63 | 
            +
              # Set job tags that get logged out in the "job_done" and "job_fail" messages.
         | 
| 64 | 
            +
              # See README for more info.
         | 
| 65 | 
            +
              # We do NOT merge the job_tags in with critical errors (death and job_error),
         | 
| 66 | 
            +
              # since those will log the job args, and they aren't properly tested right now.
         | 
| 67 | 
            +
              # We may add support in the future.
         | 
| 68 | 
            +
              def self.set_job_tags(**tags)
         | 
| 69 | 
            +
                Thread.current[:appydays_sidekiq_job_logger_job_tags] ||= {}
         | 
| 70 | 
            +
                Thread.current[:appydays_sidekiq_job_logger_job_tags].merge!(tags)
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              def self.job_tags = Thread.current[:appydays_sidekiq_job_logger_job_tags] || {}
         | 
| 74 | 
            +
             | 
| 61 75 | 
             
              def self.error_handler(ex, ctx)
         | 
| 62 76 | 
             
                # ctx looks like:
         | 
| 63 77 | 
             
                # {
         | 
| @@ -36,7 +36,7 @@ module Appydays::Loggable::SpecHelpers | |
| 36 36 | 
             
                loggers.each { |log| log.level = level }
         | 
| 37 37 |  | 
| 38 38 | 
             
                io = StringIO.new
         | 
| 39 | 
            -
                appender = SemanticLogger.add_appender(io | 
| 39 | 
            +
                appender = SemanticLogger.add_appender(io:, level:)
         | 
| 40 40 | 
             
                appender.formatter = formatter if formatter
         | 
| 41 41 | 
             
                begin
         | 
| 42 42 | 
             
                  yield
         | 
    
        data/lib/appydays/loggable.rb
    CHANGED
    
    | @@ -1,6 +1,8 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            require "semantic_logger"
         | 
| 4 | 
            +
            require "semantic_logger/formatters/raw"
         | 
| 5 | 
            +
            require "semantic_logger/formatters/json"
         | 
| 4 6 |  | 
| 5 7 | 
             
            require "appydays/version"
         | 
| 6 8 |  | 
| @@ -29,6 +31,75 @@ class SemanticLogger::Formatters::Raw | |
| 29 31 | 
             
              end
         | 
| 30 32 | 
             
            end
         | 
| 31 33 |  | 
| 34 | 
            +
            # SemanticLogger Formatter that truncates large strings in the structured log payload.
         | 
| 35 | 
            +
            # If the emitted JSON log is longer than +max_message_len+:
         | 
| 36 | 
            +
            # - the payload is walked,
         | 
| 37 | 
            +
            # - any strings with a length greater than +max_string_len+ are shortened using +shorten_string+.
         | 
| 38 | 
            +
            #   Override +shorten_string+ for custom behavior.
         | 
| 39 | 
            +
            # - any key +:stack_trace+ has its array truncated. Stack traces are very large,
         | 
| 40 | 
            +
            #   but contain short strings. Override +truncate_stack_trace+ for custom behavior.
         | 
| 41 | 
            +
            class SemanticLogger::Formatters::JsonTrunc < SemanticLogger::Formatters::Raw
         | 
| 42 | 
            +
              attr_accessor :max_message_len, :max_string_len
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              def initialize(max_message_len: 1024 * 3, max_string_len: 300, **args)
         | 
| 45 | 
            +
                super(**args)
         | 
| 46 | 
            +
                @max_message_len = max_message_len
         | 
| 47 | 
            +
                @max_string_len = max_string_len
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              def truncate_at(max_message_len, max_string_len)
         | 
| 51 | 
            +
                @max_message_len = max_message_len
         | 
| 52 | 
            +
                @max_string_len = max_string_len
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              def call(log, logger)
         | 
| 56 | 
            +
                r = super
         | 
| 57 | 
            +
                rj = r.to_json
         | 
| 58 | 
            +
                return rj if rj.length <= @max_message_len
         | 
| 59 | 
            +
                rshort = self.trim_long_strings(r)
         | 
| 60 | 
            +
                return rshort.to_json
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              def trim_long_strings(v)
         | 
| 64 | 
            +
                case v
         | 
| 65 | 
            +
                  when Hash
         | 
| 66 | 
            +
                    v.each_with_object({}) do |(hk, hv), memo|
         | 
| 67 | 
            +
                      memo[hk] =
         | 
| 68 | 
            +
                        if hk == :stack_trace && hv.is_a?(Array)
         | 
| 69 | 
            +
                          self.truncate_stack_trace(hv)
         | 
| 70 | 
            +
                        else
         | 
| 71 | 
            +
                          self.trim_long_strings(hv)
         | 
| 72 | 
            +
                        end
         | 
| 73 | 
            +
                    end
         | 
| 74 | 
            +
                  when Array
         | 
| 75 | 
            +
                    v.map { |item| self.trim_long_strings(item) }
         | 
| 76 | 
            +
                  when String
         | 
| 77 | 
            +
                    if v.size > @max_string_len
         | 
| 78 | 
            +
                      self.shorten_string(v)
         | 
| 79 | 
            +
                    else
         | 
| 80 | 
            +
                      v
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
                  else
         | 
| 83 | 
            +
                    v
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              # Given a long string, return the truncated string.
         | 
| 88 | 
            +
              # @param v [String]
         | 
| 89 | 
            +
              # @return [String]
         | 
| 90 | 
            +
              def shorten_string(v)
         | 
| 91 | 
            +
                return v[..@max_string_len] + "..."
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
              # Given a stack trace array, return the array to log.
         | 
| 95 | 
            +
              # @param arr [Array]
         | 
| 96 | 
            +
              # @return [Array]
         | 
| 97 | 
            +
              def truncate_stack_trace(arr)
         | 
| 98 | 
            +
                return arr if arr.length <= 4
         | 
| 99 | 
            +
                return [arr[0], arr[1], "skipped #{arr.length - 4} frames", arr[-2], arr[-1]]
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
            end
         | 
| 102 | 
            +
             | 
| 32 103 | 
             
            ##
         | 
| 33 104 | 
             
            # Helpers for working with structured logging.
         | 
| 34 105 | 
             
            # Use this instead of calling semantic_logger directly.
         | 
| @@ -94,8 +165,8 @@ module Appydays::Loggable | |
| 94 165 | 
             
              end
         | 
| 95 166 |  | 
| 96 167 | 
             
              module Methods
         | 
| 97 | 
            -
                def with_log_tags(tags, & | 
| 98 | 
            -
                  return SemanticLogger.named_tagged(tags, & | 
| 168 | 
            +
                def with_log_tags(tags, &)
         | 
| 169 | 
            +
                  return SemanticLogger.named_tagged(tags, &)
         | 
| 99 170 | 
             
                end
         | 
| 100 171 | 
             
              end
         | 
| 101 172 | 
             
            end
         | 
    
        data/lib/appydays/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: appydays
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.11.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Lithic Tech
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024- | 
| 11 | 
            +
            date: 2024-10-20 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: dotenv
         | 
| @@ -220,6 +220,20 @@ dependencies: | |
| 220 220 | 
             
                - - "~>"
         | 
| 221 221 | 
             
                  - !ruby/object:Gem::Version
         | 
| 222 222 | 
             
                    version: '6.0'
         | 
| 223 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 224 | 
            +
              name: simplecov
         | 
| 225 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 226 | 
            +
                requirements:
         | 
| 227 | 
            +
                - - "~>"
         | 
| 228 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 229 | 
            +
                    version: '0.22'
         | 
| 230 | 
            +
              type: :development
         | 
| 231 | 
            +
              prerelease: false
         | 
| 232 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 233 | 
            +
                requirements:
         | 
| 234 | 
            +
                - - "~>"
         | 
| 235 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 236 | 
            +
                    version: '0.22'
         | 
| 223 237 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 224 238 | 
             
              name: webmock
         | 
| 225 239 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -267,7 +281,7 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 267 281 | 
             
              requirements:
         | 
| 268 282 | 
             
              - - ">="
         | 
| 269 283 | 
             
                - !ruby/object:Gem::Version
         | 
| 270 | 
            -
                  version:  | 
| 284 | 
            +
                  version: 3.1.0
         | 
| 271 285 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 272 286 | 
             
              requirements:
         | 
| 273 287 | 
             
              - - ">="
         |