Ruby

Send logs from Ruby and Rails apps using net/http or a custom Logger device.

Quick start

No gems required — Ruby's standard net/http handles it:

ruby
require 'net/http'
require 'json'
require 'uri'

LOGFLOW_API_KEY = ENV['LOGFLOW_API_KEY']
LOGFLOW_URI = URI('https://api.getlogflow.com/v1/logs')

def log(level, message, service: 'app', **attributes)
  http = Net::HTTP.new(LOGFLOW_URI.host, LOGFLOW_URI.port)
  http.use_ssl = true
  http.open_timeout = 3
  http.read_timeout = 3

  request = Net::HTTP::Post.new(LOGFLOW_URI)
  request['Authorization'] = "Bearer #{LOGFLOW_API_KEY}"
  request['Content-Type'] = 'application/json'
  request.body = { level:, message:, service:, attributes: }.to_json

  http.request(request)
rescue StandardError
  nil # never let logging crash your app
end

log('info', 'Server started', service: 'api', port: 3000)
log('error', 'Payment failed', service: 'payments', user_id: 'u_123')

Rails integration

Create a custom Logger device and attach it to Rails.logger:

ruby
# config/initializers/logflow.rb
require 'net/http'
require 'json'

class LogFlowDevice
  SEVERITY_MAP = {
    'DEBUG' => 'debug', 'INFO' => 'info', 'WARN' => 'warn',
    'ERROR' => 'error', 'FATAL' => 'fatal', 'UNKNOWN' => 'info'
  }.freeze

  def initialize(service: Rails.application.class.module_parent_name.downcase)
    @service = service
    @uri = URI('https://api.getlogflow.com/v1/logs')
    @api_key = ENV['LOGFLOW_API_KEY']
  end

  def write(message)
    # Rails logger format: "S, [timestamp] SEVERITY -- progname: message"
    severity = message.match(/^w+,s[.*?]s+(w+)/i)&.captures&.first || 'INFO'
    clean_msg = message.gsub(/^.*?-- .*?: /, '').strip

    Thread.new do
      send_log(SEVERITY_MAP[severity] || 'info', clean_msg)
    end
  end

  def close; end

  private

  def send_log(level, message)
    http = Net::HTTP.new(@uri.host, @uri.port)
    http.use_ssl = true
    http.open_timeout = 3
    req = Net::HTTP::Post.new(@uri)
    req['Authorization'] = "Bearer #{@api_key}"
    req['Content-Type'] = 'application/json'
    req.body = { level:, message:, service: @service }.to_json
    http.request(req)
  rescue StandardError
    nil
  end
end

# Attach to Rails.logger
Rails.logger = Logger.new(LogFlowDevice.new(service: 'rails-api'))
Rails.logger.level = Rails.env.production? ? Logger::INFO : Logger::DEBUG
💡
The Thread.new wrapper makes logging non-blocking. Rails request handlers return immediately even under slow network conditions.

Batch sending with Faraday

For high-throughput apps, buffer logs and flush in a background thread:

ruby
require 'net/http'
require 'json'

class LogFlowBatcher
  def initialize(service:, flush_interval: 2)
    @service = service
    @uri = URI('https://api.getlogflow.com/v1/logs/batch')
    @api_key = ENV['LOGFLOW_API_KEY']
    @buffer = []
    @mutex = Mutex.new
    start_flush_thread(flush_interval)
  end

  def log(level, message, **attributes)
    @mutex.synchronize do
      @buffer << { level:, message:, service: @service, attributes: }
    end
  end

  private

  def start_flush_thread(interval)
    Thread.new do
      loop do
        sleep interval
        flush
      end
    end
  end

  def flush
    batch = @mutex.synchronize { @buffer.tap { @buffer = [] } }
    return if batch.empty?

    http = Net::HTTP.new(@uri.host, @uri.port)
    http.use_ssl = true
    req = Net::HTTP::Post.new(@uri)
    req['Authorization'] = "Bearer #{@api_key}"
    req['Content-Type'] = 'application/json'
    req.body = { logs: batch }.to_json
    http.request(req)
  rescue StandardError
    nil
  end
end

# Usage
$logger = LogFlowBatcher.new(service: 'api')
$logger.log('info', 'Request received', path: '/api/orders', duration_ms: 42)
$logger.log('error', 'Stripe timeout', attempt: 2)