Skip to Content
Course content

228: Structured Logging with Semantic Logger

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're running a massive warehouse. In the old days, your staff kept a "daily log" in a leather-bound notebook. They'd write things like, "Tuesday, 2 PM: A palette of blue widgets fell over in aisle 4, looks like the forklift driver was rushing." It's descriptive, sure, but if you want to find out how many palettes fell in aisle 4 over the last six months, you have to read every single page and manually tally them up. It's a nightmare.

Now, imagine replacing that notebook with a digital spreadsheet. Instead of a sentence, the worker fills out columns: [Date] [Time] [Event: Palette Drop] [Location: Aisle 4] [Item: Blue Widgets] [Cause: Speeding]. Suddenly, you can filter, sort, and generate a report on aisle 4's safety record in three seconds. That is exactly the shift we're making when we move from standard Ruby logging to structured logging with the semantic_logger gem.

The Nightmare of Grepping Text Files

Most of us start with the standard Ruby Logger. It's fine for small scripts, but in a production environment, it produces "unstructured" data—just long strings of text. I've spent way too many late nights using grep and awk to try and parse through 500MB of text files just to find a specific correlation ID for a failing request. It's tedious and error-prone.

Semantic Logger changes the game by treating a log entry as an event rather than a string. Instead of interpolating variables into a sentence, you pass them as a payload. This allows the logger to output the data in a format (like JSON) that tools like Elasticsearch, Datadog, or Splunk can index instantly.

Moving from Strings to Payloads

Let's look at how this actually looks in code. If you were logging a failed payment in a standard logger, you'd probably do something like this:

logger.error "Payment failed for user #{user_id} on order #{order_id}: #{error_message}"

That's a nightmare to parse. With Semantic Logger, we separate the message (which stays constant) from the payload (which changes). Here is how I'd write that same log:

# First, we set up the logger to output JSON to the console
SemanticLogger.add_appender(io: STDOUT, formatter: :json)
logger = SemanticLogger['PaymentProcessor']

# Now we log the event with a hash of data
logger.error "Payment failed", 
             user_id: user_id, 
             order_id: order_id, 
             error: error_message, 
             amount: 49.99

Notice the difference? The message is just "Payment failed". The metadata is a hash. When this hits your log aggregator, you don't search for the string "Payment failed for user 123"; you query for json.user_id == 123 AND level == 'error'. It's a massive jump in efficiency.

Routing Your Logs to Multiple Destinations

One thing I love about Semantic Logger is the concept of "Appenders." In the standard library, you're usually stuck sending logs to one place (a file or STDOUT). In a real-world app, you often want your logs in multiple places: a local file for quick debugging and a remote service for long-term analysis.

You can add as many appenders as you need without changing your business logic. You just configure them at startup:

# Log to a file in a human-readable format for local dev
SemanticLogger.add_appender(file_name: 'development.log', formatter: :color)

# Simultaneously stream JSON to a TCP socket for a log aggregator
SemanticLogger.add_appender(tcp_client: 'log-collector.internal:5000', formatter: :json)

Now, every time you call logger.info, the gem handles the heavy lifting of formatting that data differently for each destination. Your code stays clean, and your operations team stays happy.




📋 Practical Task

Building a Structured Order Audit Log

You are tasked with replacing a legacy logging system for an e-commerce checkout process. The current system uses string interpolation, making it impossible to track how many orders are failing due to "Out of Stock" issues versus "Payment Declined" issues.

Your Goal: Create a script that uses semantic_logger to log a series of order attempts.

  • Configure the logger to output JSON to STDOUT.
  • Create a loop that simulates processing three different orders.
  • For each order, log an info message "Processing Order" and an error message "Order Failed" if the order fails.
  • The error log must include a payload with: order_id, customer_email, and a reason (e.g., :insufficient_funds, :out_of_stock).

Verify that your output is valid JSON, meaning you should see curly braces {} and key-value pairs in your console, not just plain sentences.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.