---
url: https://simpledatalogs.com/docs/quickstarts/rails.md
description: >-
  Send events and Rails log lines to simpledatalogs with a small HTTP recipe and
  a logger device.
---

# Rails

::: warning HTTP recipe
There is no Ruby gem yet. This page posts to the [ingest API](/api/ingest) with `Net::HTTP`. It batches in memory and drops a batch it can't send; for guaranteed delivery of log files, use [Vector or Fluent Bit](/integrations/log-shippers).
:::

## Install

Nothing to install. Add the key to your environment:

```sh
SIMPLEDATALOGS_KEY=sdl_sec_…
```

## Add it

```ruby
# config/initializers/simpledatalogs.rb
require "json"
require "net/http"
require "securerandom"

module SimpleDataLogs
  INGEST_URL = URI(ENV.fetch("SIMPLEDATALOGS_URL", "https://edge.simpledatalogs.com/v1/ingest"))
  PENDING = Queue.new

  def self.track(event, props = {}, user_id: nil)
    line = { event: event, event_id: SecureRandom.uuid, ts: Time.now.utc.iso8601(3), props: props }
    line[:user_id] = user_id.to_s if user_id
    PENDING << line.to_json
  end

  def self.flush
    lines = []
    lines << PENDING.pop(true) while lines.size < 50 && !PENDING.empty?
    return if lines.empty?

    Net::HTTP.post(INGEST_URL, lines.join("\n"),
                   "Authorization" => "Bearer #{ENV.fetch('SIMPLEDATALOGS_KEY')}",
                   "Content-Type" => "application/x-ndjson")
  rescue StandardError => error
    warn "[simpledatalogs] batch dropped: #{error.message}"
  end

  # A logger device: each formatted line is already one JSON log line.
  class LogDevice
    def write(line) = PENDING << line.chomp
    def close; end
  end

  def self.logger(level: :warn)
    logger = ActiveSupport::Logger.new(LogDevice.new, level: level)
    logger.formatter = proc do |severity, time, _progname, message|
      { event_id: SecureRandom.uuid, ts: time.utc.iso8601(3), level: severity.downcase, message: message.to_s }.to_json + "\n"
    end
    logger
  end

  Thread.new { loop { sleep 5; flush } }
  at_exit { flush }
end

# Rails 7.1+: warnings and errors also go to simpledatalogs.
Rails.logger.broadcast_to(SimpleDataLogs.logger)
```

```ruby
SimpleDataLogs.track("signup", { plan: "pro" }, user_id: current_user.id)
```

## In the first minute

* Your `track()` events within 5 seconds.
* Rails warnings and errors on the **Logs** tab (`WARN` reads as `warning`, `FATAL` as `critical`).

## Autocapture

None: this recipe sends only what you track and what the logger writes at its level. With several Puma workers, each process sends its own batches.
