Class: DataShifter::Internal::LogDeduplicator

Inherits:
Object
  • Object
show all
Defined in:
lib/data_shifter/internal/log_deduplicator.rb

Overview

A proxy logger that suppresses repeated log messages during a shift run. Uses a hash of the message as the key for memory efficiency. First occurrence is forwarded; subsequent occurrences are counted but not forwarded. At the end, prints a summary of suppressed messages via puts.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(real_logger, cap:) ⇒ LogDeduplicator

Returns a new instance of LogDeduplicator.



15
16
17
18
19
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 15

def initialize(real_logger, cap:)
  @real_logger = real_logger
  @cap = cap
  @seen = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method) ⇒ Object



107
108
109
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 107

def method_missing(method, ...)
  @real_logger.send(method, ...)
end

Instance Attribute Details

#capObject (readonly)

Returns the value of attribute cap.



13
14
15
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 13

def cap
  @cap
end

#real_loggerObject (readonly)

Returns the value of attribute real_logger.



13
14
15
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 13

def real_logger
  @real_logger
end

#seenObject (readonly)

Returns the value of attribute seen.



13
14
15
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 13

def seen
  @seen
end

Class Method Details

.with_deduplicating_logger(real_logger, cap:) ⇒ Object



116
117
118
119
120
121
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 116

def with_deduplicating_logger(real_logger, cap:)
  proxy = new(real_logger, cap:)
  yield proxy
ensure
  proxy&.print_summary
end

Instance Method Details

#<<(msg) ⇒ Object



59
60
61
62
63
64
65
66
67
68
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 59

def <<(msg)
  key = message_key(Logger::INFO, nil, msg)
  if @seen.key?(key)
    @seen[key][:count] += 1
  else
    enforce_cap
    @seen[key] = { count: 1, message: truncate_message(msg), severity: Logger::INFO }
    @real_logger << msg
  end
end

#add(severity, message = nil, progname = nil, &block) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 21

def add(severity, message = nil, progname = nil, &block)
  msg = block ? block.call : message
  key = message_key(severity, progname, msg)

  if @seen.key?(key)
    @seen[key][:count] += 1
    nil
  else
    enforce_cap
    @seen[key] = { count: 1, message: truncate_message(msg || progname), severity: }
    @real_logger.add(severity, message, progname, &block)
  end
end

#closeObject



86
87
88
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 86

def close
  @real_logger.close
end

#debug(message = nil, progname = nil) ⇒ Object



35
36
37
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 35

def debug(message = nil, progname = nil, &)
  add(Logger::DEBUG, message, progname, &)
end

#error(message = nil, progname = nil) ⇒ Object



47
48
49
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 47

def error(message = nil, progname = nil, &)
  add(Logger::ERROR, message, progname, &)
end

#fatal(message = nil, progname = nil) ⇒ Object



51
52
53
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 51

def fatal(message = nil, progname = nil, &)
  add(Logger::FATAL, message, progname, &)
end

#formatterObject



78
79
80
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 78

def formatter
  @real_logger.formatter
end

#formatter=(val) ⇒ Object



82
83
84
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 82

def formatter=(val)
  @real_logger.formatter = val
end

#info(message = nil, progname = nil) ⇒ Object



39
40
41
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 39

def info(message = nil, progname = nil, &)
  add(Logger::INFO, message, progname, &)
end

#levelObject



70
71
72
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 70

def level
  @real_logger.level
end

#level=(val) ⇒ Object



74
75
76
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 74

def level=(val)
  @real_logger.level = val
end


94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 94

def print_summary
  suppressed = suppressed_messages
  return if suppressed.empty?

  puts "\n[DataShifter] Suppressed repeated log messages:"
  suppressed.each_value do |entry|
    count = entry[:count] - 1
    snippet = entry[:message].to_s[0, 100]
    snippet = "#{snippet}..." if entry[:message].to_s.length > 100
    puts "  #{count}x suppressed: #{snippet.inspect}"
  end
end

#respond_to_missing?(method, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


111
112
113
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 111

def respond_to_missing?(method, include_private = false)
  @real_logger.respond_to?(method, include_private) || super
end

#suppressed_messagesObject



90
91
92
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 90

def suppressed_messages
  @seen.select { |_k, v| v[:count] > 1 }
end

#unknown(message = nil, progname = nil) ⇒ Object



55
56
57
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 55

def unknown(message = nil, progname = nil, &)
  add(Logger::UNKNOWN, message, progname, &)
end

#warn(message = nil, progname = nil) ⇒ Object



43
44
45
# File 'lib/data_shifter/internal/log_deduplicator.rb', line 43

def warn(message = nil, progname = nil, &)
  add(Logger::WARN, message, progname, &)
end