appydays 0.9.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 191701f151a4b25333ee5f6cc1262e70c4d62dc063350bbebd4d00798cb2128e
4
- data.tar.gz: 05d188d89e2db3047837f59784773284aa194f9435c2d6f83a8d8bc84b7c06d3
3
+ metadata.gz: f7233dee300e207e49803a368896ad6b098641db88b703d54860002055609826
4
+ data.tar.gz: 8c1d7694c1ab1695526676690900f1d5d12facee1adb4b3a823d409af22606a2
5
5
  SHA512:
6
- metadata.gz: dd5f3a2c5ebfc6d172992e98ed34d6eb33a581b5ef3c55cb0744491cdb22274c9e519af561aba1014dedd68606be9b714e8c7bf5aa9d6deb4b849cbd7115ec1c
7
- data.tar.gz: fd5c9afa41fa2d9ededce48760d147339029c29c21e9679e3d58cbf43f544419a95201634c8626413718da52eac91262fead6515361e2fd7017901e355ed5171
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: 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: 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, &block)
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, &block)
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: io, level: level)
39
+ appender = SemanticLogger.add_appender(io:, level:)
40
40
  appender.formatter = formatter if formatter
41
41
  begin
42
42
  yield
@@ -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, &block)
98
- return SemanticLogger.named_tagged(tags, &block)
168
+ def with_log_tags(tags, &)
169
+ return SemanticLogger.named_tagged(tags, &)
99
170
  end
100
171
  end
101
172
  end
@@ -85,7 +85,7 @@ module Appydays::SpecHelpers
85
85
  end
86
86
 
87
87
  protected def change_tz(t, zone)
88
- return t.change(zone: zone) if t.respond_to?(:change)
88
+ return t.change(zone:) if t.respond_to?(:change)
89
89
  prev_tz = ENV.fetch("TZ", nil)
90
90
  begin
91
91
  ENV["TZ"] = zone
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appydays
4
- VERSION = "0.9.0"
4
+ VERSION = "0.11.0"
5
5
  end
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.9.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-04-15 00:00:00.000000000 Z
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: 2.7.0
284
+ version: 3.1.0
271
285
  required_rubygems_version: !ruby/object:Gem::Requirement
272
286
  requirements:
273
287
  - - ">="