Class: Lens::Rails::LogExporter
- Inherits:
-
Logger
- Object
- Logger
- Lens::Rails::LogExporter
- Defined in:
- lib/lens/rails/log_exporter.rb
Overview
Logger sink that batches log records and ships them to Lens via POST /v1/logs. Wired into Rails.logger via broadcast in the Railtie.
Constant Summary collapse
- SEVERITY_TEXT =
{ ::Logger::DEBUG => "DEBUG", ::Logger::INFO => "INFO", ::Logger::WARN => "WARN", ::Logger::ERROR => "ERROR", ::Logger::FATAL => "FATAL", ::Logger::UNKNOWN => "UNKNOWN" }.freeze
- DROP_WARN_INTERVAL =
60.0
Instance Method Summary collapse
- #add(severity, message = nil, progname = nil, &block) ⇒ Object
- #flush ⇒ Object
-
#initialize(url:, token:, service_name:, max_buffer: 10_000, min_severity: ::Logger::INFO, open_timeout: 2, read_timeout: 2) ⇒ LogExporter
constructor
A new instance of LogExporter.
- #restart_flush_thread ⇒ Object
- #shutdown(timeout:) ⇒ Object
Constructor Details
#initialize(url:, token:, service_name:, max_buffer: 10_000, min_severity: ::Logger::INFO, open_timeout: 2, read_timeout: 2) ⇒ LogExporter
Returns a new instance of LogExporter.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/lens/rails/log_exporter.rb', line 22 def initialize(url:, token:, service_name:, max_buffer: 10_000, min_severity: ::Logger::INFO, open_timeout: 2, read_timeout: 2) super(File::NULL) @token = token @service_name = service_name @uri = URI("#{url}/v1/logs") @max_buffer = max_buffer @open_timeout = open_timeout @read_timeout = read_timeout @min_severity = min_severity @buffer = [] @dropped = 0 @last_drop_warn_at = 0.0 @mutex = Mutex.new restart_flush_thread Lens::Rails.register_flushable(self) end |
Instance Method Details
#add(severity, message = nil, progname = nil, &block) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/lens/rails/log_exporter.rb', line 43 def add(severity, = nil, progname = nil, &block) return true if Thread.current[:lens_in_export] severity ||= ::Logger::UNKNOWN return true if severity < @min_severity = block ? block.call : progname if .nil? return true if .nil? record = { timestamp: Time.now.iso8601(3), severity: SEVERITY_TEXT[severity] || "UNKNOWN", body: .to_s.gsub(/\e\[[0-9;]*m/, ""), controller: Thread.current[:lens_controller], action: Thread.current[:lens_action], request_id: Thread.current[:lens_request_id] }.compact dropped_now = 0 @mutex.synchronize do if @buffer.size >= @max_buffer @buffer.shift @dropped += 1 dropped_now = @dropped end @buffer << record end maybe_warn_dropped(dropped_now) if dropped_now.positive? true end |
#flush ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/lens/rails/log_exporter.rb', line 75 def flush records = @mutex.synchronize { @buffer.tap { @buffer = [] } } return if records.empty? body = JSON.generate(logs: records, service: @service_name, version: Lens::Rails::VERSION) req = Net::HTTP::Post.new(@uri) req["Content-Type"] = "application/octet-stream" req["Authorization"] = "Bearer #{@token}" req.body = Zlib.gzip(body) Thread.current[:lens_in_export] = true begin Net::HTTP.start(@uri.host, @uri.port, use_ssl: @uri.scheme == "https", open_timeout: @open_timeout, read_timeout: @read_timeout) { |http| http.request(req) } ensure Thread.current[:lens_in_export] = false end end |
#restart_flush_thread ⇒ Object
102 103 104 |
# File 'lib/lens/rails/log_exporter.rb', line 102 def restart_flush_thread BackoffLoop.new(base: 5, max: 60, name: "lens-log-flush").start { flush } end |
#shutdown(timeout:) ⇒ Object
97 98 99 100 |
# File 'lib/lens/rails/log_exporter.rb', line 97 def shutdown(timeout:) Thread.new { flush }.join(timeout) rescue end |