<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Razvan</title>
    <description>The latest articles on DEV Community by Razvan (@dezsirazvan).</description>
    <link>https://dev.to/dezsirazvan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3702342%2F64b9bcf5-04a8-4110-825c-702645414efb.png</url>
      <title>DEV Community: Razvan</title>
      <link>https://dev.to/dezsirazvan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dezsirazvan"/>
    <language>en</language>
    <item>
      <title>Your product can answer questions about itself now</title>
      <dc:creator>Razvan</dc:creator>
      <pubDate>Sat, 27 Jun 2026 13:07:39 +0000</pubDate>
      <link>https://dev.to/dezsirazvan/your-product-can-answer-questions-about-itself-now-394a</link>
      <guid>https://dev.to/dezsirazvan/your-product-can-answer-questions-about-itself-now-394a</guid>
      <description>&lt;p&gt;Ask your product anything, in plain English, and it answers. Then it keeps watching and tells you the moment the answer changes.&lt;/p&gt;

&lt;p&gt;That's the whole thing. It sounds small until you sit with what it replaces.&lt;/p&gt;

&lt;p&gt;Think about the last time you needed to know something about your own product. Whether the feature you shipped is actually being used. Whether the customer about to churn still logs in. How many people hit an error today. The answer always existed somewhere inside the system. You just couldn't get it without interrupting the one person who knows where to look, and waiting.&lt;/p&gt;

&lt;p&gt;That waiting is a tax every company pays and nobody notices. The founder waits a day for the analyst's dashboard. Support waits twenty minutes per ticket for an engineer. Sales waits for someone to check whether the dying account is even awake. The product had the answer the entire time. It just couldn't speak.&lt;/p&gt;

&lt;p&gt;Now it can.&lt;/p&gt;

&lt;h2&gt;
  
  
  You ask. It watches. It tells you.
&lt;/h2&gt;

&lt;p&gt;In EZLogs you pin a question to your product, written exactly how you'd say it out loud:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Tell me when a trial account goes quiet for 3 days."&lt;br&gt;
"Is the bulk-export feature failing for anyone?"&lt;br&gt;
"Tell me when a new user registers."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No metric to name. No query to write. No threshold to set. No dashboard to build first. You type the sentence, and the product takes it from there.&lt;/p&gt;

&lt;p&gt;From that moment it's watching. And when the answer changes, it comes to you, in plain language, with the story already written:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Acme Corp went quiet.&lt;/strong&gt; Trial, day 9. Zero activity for 3 days, then 4 failed logins this morning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bulk-export is failing for 5 people.&lt;/strong&gt; 47 ran it this week. 5 failed, all on exports over 10,000 rows, the same step every time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A salesperson catches the churning account while there's still time to call. A designer learns their feature is quietly broken for everyone on big files, without instrumenting a single line. A founder gets the business in a sentence every Monday, and an off-cycle nudge the moment something moves. Same engine. Everyone in the company finally on speaking terms with the product they're building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every number shows its receipts
&lt;/h2&gt;

&lt;p&gt;Here's the part that makes it trustworthy instead of just clever.&lt;/p&gt;

&lt;p&gt;Every figure in every answer clicks straight back to the real events behind it. "3 checkouts failed for Acme today" takes you to those exact three checkouts. "47 people ran bulk-export" takes you to the 47. The claim and the proof are always one click apart.&lt;/p&gt;

&lt;p&gt;These numbers are not an AI guessing what probably happened. They are counted from what actually happened. The whole alert is one click that drops you exactly where the evidence lives: a trend alert lands you on the trend, a failure alert on the failures, a "this account went silent" alert on that account's live page. One headline, one click, the full story waiting on the other side.&lt;/p&gt;

&lt;p&gt;The AI does exactly one job, at the edges: it turns your sentence into something the system can answer, and it phrases the result back in plain English. It never decides what's true. The truth is a fact, counted, with its receipts attached. The AI translates, it never judges, and that is what lets a non-engineer trust the answer the way they'd trust a number an engineer hand-pulled for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;Datadog tells your engineers when a number crosses a line they configured. This lets anyone in your company ask the product a question in human words and get a true, cited, plain-language answer that keeps watching for them.&lt;/p&gt;

&lt;p&gt;Different audience. Different promise. A different relationship with the software you build.&lt;/p&gt;

&lt;p&gt;I think the products that win the next few years won't feel like magic because of the AI. They'll feel like magic because they finally tell you the truth, in your own words, the moment it changes. That's what we shipped. It's live in EZLogs today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;There's a free tier, no card. If you've got a Rails or Next.js app, point the agent at it, pin a question, and see if the answer holds up when it fires.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ezlogs.io" rel="noopener noreferrer"&gt;https://ezlogs.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you pin something and the alert comes back wrong, or worse, comes back confidently fake, tell me. That's the one bug this whole thing exists to not have.&lt;/p&gt;

&lt;p&gt;Razvan&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rails</category>
      <category>ruby</category>
      <category>devtools</category>
    </item>
    <item>
      <title>How to give Claude (or Cursor) access to your Rails app's activity logs</title>
      <dc:creator>Razvan</dc:creator>
      <pubDate>Sun, 14 Jun 2026 09:08:20 +0000</pubDate>
      <link>https://dev.to/dezsirazvan/how-to-give-claude-or-cursor-access-to-your-rails-apps-activity-logs-38m3</link>
      <guid>https://dev.to/dezsirazvan/how-to-give-claude-or-cursor-access-to-your-rails-apps-activity-logs-38m3</guid>
      <description>&lt;p&gt;Ask Claude this, today, with no setup:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What did user 4421 do in our app yesterday?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You will get an answer. It will be confident, specific, and completely made up. Claude has no access to your production database. So it does what a language model does when it has no facts: it pattern-matches what an answer &lt;em&gt;should&lt;/em&gt; look like and hands you fiction in a calm voice.&lt;/p&gt;

&lt;p&gt;That is the whole problem. Not that the model is dumb. That it is answering a question about your data without your data.&lt;/p&gt;

&lt;p&gt;The Model Context Protocol fixes this, if you point it at the right source. This post is about what MCP is for a Rails dev who hasn't wired one up yet, why you'd want one over your app's activity log, and how I built the EZLogs MCP server so the answer is the boring true one instead of the confident fake one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What MCP actually is
&lt;/h2&gt;

&lt;p&gt;MCP is a small open protocol from Anthropic. It lets an AI client (Claude Desktop, Cursor, your internal copilot) call out to a server you control and ask for data, mid-conversation, before it answers you.&lt;/p&gt;

&lt;p&gt;That's it. It is not a framework. It is not a model. It is a way for the model to say "hold on, let me go look" instead of guessing.&lt;/p&gt;

&lt;p&gt;A server exposes three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tools&lt;/strong&gt; the AI can call, like "find actions by this user between these dates"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resources&lt;/strong&gt; it can read, like "the 50 most recent significant actions"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompts&lt;/strong&gt; it can use, like "draft an incident report for this action"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI decides when to call them. You decide what they return. The protocol is just JSON-RPC over HTTP at a single endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why your activity log is the right thing to expose
&lt;/h2&gt;

&lt;p&gt;You could point an MCP server at your raw database. People do. It goes badly for the same reason raw logs go badly for humans: the model gets &lt;code&gt;gid://app/Order/4421&lt;/code&gt;, a pile of foreign keys, and a Sidekiq job class, and now &lt;em&gt;it&lt;/em&gt; has to do the translation. So it guesses at the join, guesses at what the columns mean, and you are back to a confident fake answer, just one layer deeper.&lt;/p&gt;

&lt;p&gt;An activity log is already the translated layer. One row per user action, correlated across HTTP, background jobs, and database changes, with a human-readable name. "User 4421 tried to ship order #4421, address validation failed, the retry job ran three times and gave up." When the model reads &lt;em&gt;that&lt;/em&gt;, it has nothing left to invent. The facts are already facts.&lt;/p&gt;

&lt;p&gt;So the activity log is the better MCP source precisely because the hard part, correlation and naming, was done before the model showed up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that matters: deterministic, not generated
&lt;/h2&gt;

&lt;p&gt;Here is the line I care about most, and the one I'd push back on hardest if you were evaluating any tool in this space.&lt;/p&gt;

&lt;p&gt;The translation in EZLogs has no model in it.&lt;/p&gt;

&lt;p&gt;The cards your team reads, and the data the MCP server returns, are produced by correlation plus templates. Same events in, same sentence out, every time. The action stream is folded into state with pure functions that never call an LLM and never write to the database, and every value carries the IDs of the specific events it came from. You can click any number back to its evidence.&lt;/p&gt;

&lt;p&gt;That means when your AI asks the EZLogs MCP server "what happened to order 4421," the rows it gets back are deterministic facts, not a second model's opinion. Your AI narrates them. Our side never narrates. We hand over structured data with citations; the model turns it into a sentence and spends its own credits doing it.&lt;/p&gt;

&lt;p&gt;This is the difference between "the AI made up an answer" and "the AI read the answer." A year into shipping AI features, that is the distinction CTOs are actually asking about. Where do the facts come from, and can I trace them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the EZLogs MCP server exposes
&lt;/h2&gt;

&lt;p&gt;Six query-only tools. Every one of them reads. None of them writes back into your app, ever. That is a permanent design rule, not a current limitation: EZLogs holds no write credentials into your systems, so the blast radius of the whole thing is read-only.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;find_actions&lt;/strong&gt;: search the activity log by actor, entity, outcome, date, significance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;get_action&lt;/strong&gt;: one action by id, with its full underlying event stream&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;actor_timeline&lt;/strong&gt;: one user's or agent's recent actions plus their current state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;entity_timeline&lt;/strong&gt;: one order, account, document, whatever, plus its current state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;compare_actions&lt;/strong&gt;: line up two to five actions and find where they diverged&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;top_lists&lt;/strong&gt;: rank actors and entities by activity in a window&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;find_actions&lt;/code&gt; and &lt;code&gt;top_lists&lt;/code&gt; are on the free tier. The rest are paid. MCP usage itself is unlimited on every paid tier, because the AI spends its own credits to narrate; we just deliver the rows.&lt;/p&gt;

&lt;p&gt;There are also read-only resources (&lt;code&gt;ezlogs://recent&lt;/code&gt;, &lt;code&gt;ezlogs://actor/{id}&lt;/code&gt;, and friends) and prebuilt prompts (&lt;code&gt;summarize_day&lt;/code&gt;, &lt;code&gt;investigate_failure&lt;/code&gt;, &lt;code&gt;incident_report&lt;/code&gt;) for the common questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring it up
&lt;/h2&gt;

&lt;p&gt;Two steps. Get events flowing, then connect your AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rails app&lt;/strong&gt; (the gem captures HTTP, Sidekiq, and ActiveRecord with no manual middleware):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Gemfile&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'ez_logs_agent'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle &lt;span class="nb"&gt;install
&lt;/span&gt;rails generate ez_logs_agent:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/ez_logs_agent.rb&lt;/span&gt;
&lt;span class="no"&gt;EzLogsAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server_url&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://app.ezlogs.io"&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;project_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ezl_your_key_here"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Next.js app&lt;/strong&gt;, if that's your stack instead or as well, is &lt;code&gt;npm install ezlogs-nextjs&lt;/code&gt; plus a small &lt;code&gt;instrumentation.ts&lt;/code&gt;. Both agents emit the identical wire format, so the server treats them as one source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then connect Claude or Cursor.&lt;/strong&gt; For Cursor, add to &lt;code&gt;~/.cursor/mcp.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ezlogs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://app.ezlogs.io/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer ezl_your_key_here"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Claude Desktop on macOS it's one command, which backs up your existing config and merges in the EZLogs entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://app.ezlogs.io/install/claude-desktop.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quit Claude, reopen, and ask it "what happened yesterday?" This time it goes and looks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it isn't
&lt;/h2&gt;

&lt;p&gt;It isn't a metrics platform and it isn't an APM. It doesn't watch CPU or page you at 3am. Datadog watches your infrastructure; this explains the work your app did, in language a support person can read.&lt;/p&gt;

&lt;p&gt;It also isn't an AI tool that translates your logs. The AI is on &lt;em&gt;your&lt;/em&gt; side of the connection, the one asking questions. The translation underneath is deterministic and the same with the AI turned off. If removing every model from the path would break the answer, the design would be wrong. It doesn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;There's a free tier, no card, and the MCP server is part of it. If you've got a Rails or Next.js app and an AI client you keep wanting to ask about production, point one at the other and see if the answer holds up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ezlogs.io" rel="noopener noreferrer"&gt;https://ezlogs.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you wire it up and the answer comes back wrong, or worse, comes back confidently fake, tell me. That's the one bug this whole thing exists to not have.&lt;/p&gt;

&lt;p&gt;Razvan&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rails</category>
      <category>ruby</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Every engineering team has a translator. I wrote the thing that translates</title>
      <dc:creator>Razvan</dc:creator>
      <pubDate>Fri, 05 Jun 2026 13:59:07 +0000</pubDate>
      <link>https://dev.to/dezsirazvan/every-engineering-team-has-a-translator-i-wrote-the-thing-that-translates-1557</link>
      <guid>https://dev.to/dezsirazvan/every-engineering-team-has-a-translator-i-wrote-the-thing-that-translates-1557</guid>
      <description>&lt;p&gt;Every engineering team I've worked on has had the same unspoken job title. Nobody's hired into it. It just lands on whoever's been there longest and answers Slack the fastest.&lt;/p&gt;

&lt;p&gt;The translator.&lt;/p&gt;

&lt;p&gt;Someone in support pings: "what happened to order #4421?" The translator opens five tabs. Application logs. Sidekiq dashboard. Database. A few greps. Twenty minutes later they paste back: "shipping address validation failed because the customer's postcode hit the new regex, then the retry job ran 3 times, then we rolled back the order."&lt;/p&gt;

&lt;p&gt;The support person says thanks. The customer gets an answer. The translator goes back to whatever they were doing before they got pulled out of focus.&lt;/p&gt;

&lt;p&gt;This is the actual cost of "observability." Not the bill from Datadog. The bill from your senior engineer's attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  What logs are vs. what humans need
&lt;/h2&gt;

&lt;p&gt;Logs are designed for the computer that wrote them. Stack traces. Request IDs. Job classes. Foreign keys. A line in a log file is a diff against some piece of internal state, formatted so the system can find it later. That's correct. That's what logs are for.&lt;/p&gt;

&lt;p&gt;It's also why nobody outside engineering can read them.&lt;/p&gt;

&lt;p&gt;A product manager doesn't care that &lt;code&gt;Sidekiq::Worker#perform&lt;/code&gt; ran with &lt;code&gt;args=["gid://app/Order/4421", "ship"]&lt;/code&gt;. They care that the shipping label generation for Razvan's order failed because the address didn't validate. Same event. Two completely different representations.&lt;/p&gt;

&lt;p&gt;The gap between those two representations is where the translator lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  ezlogs is the translator
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://ezlogs.io" rel="noopener noreferrer"&gt;ezlogs&lt;/a&gt; (a Ruby gem and Next.js companion) because I was tired of being the translator. The premise is small. The gem captures three things you already have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP requests (what users and external systems asked for)&lt;/li&gt;
&lt;li&gt;Background jobs (what the system did in response)&lt;/li&gt;
&lt;li&gt;Model callbacks (what changed in the database)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then it correlates them into a single "action" with a human-readable name. Not "POST /orders/4421/ship returned 422." Instead: "Razvan tried to ship order #4421. Address validation failed. The retry job ran 3 times and gave up."&lt;/p&gt;

&lt;p&gt;Same data. The pipeline expands GIDs into names, groups events that share a correlation ID, and renders a deterministic sentence from a template. No model in the loop. The output is reproducible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it isn't
&lt;/h2&gt;

&lt;p&gt;It isn't a metrics platform. It doesn't replace Datadog or your APM. It doesn't watch CPU. It doesn't page you at 3am.&lt;/p&gt;

&lt;p&gt;It also doesn't write back into your app. No "pause" buttons. No approval queues. No webhooks. The agent reads. That's it. If you want to act on what ezlogs sees, your existing ops tooling does the writing. That's deliberate. A read-only product has a tiny blast radius and a tiny security review.&lt;/p&gt;

&lt;p&gt;The job is to explain. The job is to make the activity log something a support person, a PM, or a founder can read without asking an engineer for help.&lt;/p&gt;

&lt;h2&gt;
  
  
  The boring middle
&lt;/h2&gt;

&lt;p&gt;ezlogs is not an "AI translation tool for logs." The translation is deterministic. Templates plus correlation, same input in, same sentence out, no model in the loop. AI summaries are an optional paid add-on, off by default. The free tier already includes the activity log and an MCP server so your AI agents (Claude, Cursor, internal copilots) can query what happened.&lt;/p&gt;

&lt;p&gt;The actual work has been correlation. Making sure the Sidekiq job that ran 4 seconds after the HTTP request gets stitched to that request. Making sure a GlobalID in a job arg resolves back to "User #42 (Razvan)" instead of &lt;code&gt;gid://app/User/42&lt;/code&gt;. Making sure the same event captured by the Ruby agent and the Next.js agent produces byte-identical wire format on the server.&lt;/p&gt;

&lt;p&gt;None of it demos well. All of it has to be right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it is
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://rubygems.org/gems/ez_logs_agent" rel="noopener noreferrer"&gt;ez_logs_agent&lt;/a&gt; the Ruby gem is on RubyGems. The &lt;a href="https://www.npmjs.com/package/ezlogs-nextjs" rel="noopener noreferrer"&gt;Next.js companion&lt;/a&gt; is on npm. The hosted dashboard is live at &lt;a href="https://ezlogs.io" rel="noopener noreferrer"&gt;ezlogs.io&lt;/a&gt;. Free tier includes the deterministic activity log and the MCP server. Sign up, point an agent at your app, see what it has been doing.&lt;/p&gt;




&lt;p&gt;If you want to try it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ezlogs.io" rel="noopener noreferrer"&gt;ezlogs.io&lt;/a&gt; (free tier, no card)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://rubygems.org/gems/ez_logs_agent" rel="noopener noreferrer"&gt;ez_logs_agent&lt;/a&gt; (Ruby gem)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/ezlogs-nextjs" rel="noopener noreferrer"&gt;ezlogs-nextjs&lt;/a&gt; (npm)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've ever been the translator on your team and want to stop, say hi.&lt;/p&gt;

&lt;p&gt;Razvan&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>webdev</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
