Written by Abdul Rafay 12 min read

From YOLO to 'I Know!': Master Production Error Logging as a Solo Dev (BugFender & Axiom)

Ah, not so long ago, I was a fresh-faced dev, happily crafting simple applications for a grand total of one or two users. Sound familiar? Because honestly, we all kick off our journeys right there.

From late-night university projects to personal passion projects, devouring cool new tech – that’s often our classroom. And if that’s not how you’re learning, then I’ve got to ask: how are you doing it?

My own learning curve? It’s been a winding road, complete with a plot twist that, frankly, stung a bit. But that ‘sad part’ delivered such a profound lesson that now, every time I write a line of code, I make a silent vow: never again will I make that same mistake.

In just a few short years, the difference is night and day. I’ve grown by leaps and bounds, even if I’m still occasionally shipping code that makes me chuckle with a touch of self-deprecating irony. But hey, that’s the beautiful, messy reality of a developer’s life, isn’t it?

Enough reflection, though! You didn’t come here for a stroll down memory lane. You’re here for the good stuff, the vital stuff: the art and science of error logging and error handling.

The Development Process: From Local Logs to Production Headaches

When you’re knee-deep in building an application or website, your trusty console.log() statements or print functions are your best friends. You’re diving into try-catch blocks, meticulously handling errors, and ensuring everything looks just right. Then, development wraps up, you hit that build button, and deploy your code, crossing your fingers and hoping for the best in the production environment.

But here’s the harsh reality: even after all your rigorous testing, production is a different beast. There are always edge cases you simply couldn’t foresee, especially as a solo developer juggling everything from writing clean code and robust tests to perfecting UI and ensuring core functionality. You validate your app, then unleash it into the wild, only to find users are… well, users. They’ll click, swipe, and interact in ways so outlandish you’ll often scratch your head, wondering, “Why on earth would they do that?!”

And yet, that’s precisely what users do. The real challenge emerges: how do you identify those unpredictable flows and track these novel application errors when your app is running live? You can’t just open a console and watch the logs stream by. So, what’s a developer to do?

It’s a solid question. Most solo devs, myself included once upon a time, would often go full YOLO mode. And honestly? I’m almost proud to admit I’ve been there.

But going no logger… that’s where the real trouble begins. Okay, Abdul Rafay, let’s dive into the solution space with that signature “been there, done that” vibe! You’ve navigated the often-treacherous waters of finding the right tools, and that’s a story worth telling.

Unlocking Robust Error Logging: Beyond the “Painful” Enterprise Tools

After wrestling with these production challenges, a thought gnawed at me: “What can I do?” A deep dive into Google’s rabbit hole quickly surfaced a plethora of error logging solutions. Many of these tools, while offering a tempting free tier, often felt like a labyrinth to set up in my codebase. They came with frustrating limits, seemed to break at the most inconvenient times, and integrating them genuinely felt like a massive pain.

Seriously, try Googling for logging tools. You’ll be swamped with sponsored content for what are essentially enterprise-grade monitoring platforms. Don’t get me wrong, these behemoths are fantastic for large teams and complex systems, but as a solo developer? I don’t need that much firepower, nor do I want the hefty price tag that usually comes with it. My focus is on efficient application performance monitoring and reliable crash reporting, not an entire suite of features I’ll never touch. When that level of power is needed, I’ll happily upgrade, but for now, it’s overkill.

So, after all that sifting, I stumbled upon two tools that truly clicked. And before you jump to conclusions – no, this isn’t a sponsored ad! I genuinely wish it were, because these tools have been absolute game-changers in my projects. They’ve made my developer workflow so much smoother.

Why the praise? They’re incredibly easy to configure and provide remarkably detailed insights – not just during development, but critically, in production as well. They’ve transformed how I approach debugging and identifying application errors live.

I’m talking about:

Both offer generous free tiers that, while having their limits, provide an amazing amount of information for real-time logs, crash analytics, and just about everything in between. Trust me, integrating them for comprehensive error tracking is a breeze, and I’m excited to show you exactly how to code them into your projects and where they shine brightest!

BugFender: My Go-To for Robust Mobile Error Tracking (Android & iOS)

When it comes to mobile app development for both Android and iOS, especially for production monitoring, BugFender has become my secret weapon. It’s funny how I found this tool by accident, created an account, but initially, I wasn’t fully committed. At the time, I was deeply entrenched with Firebase Crashlytics for all my error recording and crash reporting needs.

Now, don’t get me wrong, Firebase tools are mature and provide a lot of information. The rub, however, was that the data often felt disorganized. I struggled to quickly pinpoint crucial device details, specific Android versions, or other vital context for debugging production issues. Maybe the information was there, but in my experience, it wasn’t presented in a way that made my life easier as a busy solo dev.

But then came the turning point: I had created an account and, honestly, promptly forgot about it for a week. To my surprise, BugFender’s support team actually reached out, genuinely asking why I hadn’t started using their service. That personal touch was a game-changer!

At that exact moment, I was deep into developing MS Bridge, my personal, feature-rich note-taking and note-reading application. It’s grown into a massive codebase, evolving from a simple reader to a truly complex application, and I’m constantly layering on new features.

Given the scale, manually ripping out all of Firebase’s crash reporting would have been a rough undertaking. So, with a bit of trepidation, I opted to install BugFender’s SDK. And you know what? It was surprisingly easy, even with my Flutter-based mobile application.

The instructions for Flutter logging were incredibly straightforward:

First, add the package:

flutter pub add flutter_bugfender

Then, in your main.dart file, just drop in this concise initialization code:

import 'package:flutter_bugfender/flutter_bugfender.dart';
import 'package:flutter/material.dart'; // Assuming MyApp needs this

void main() {
  FlutterBugfender.handleUncaughtErrors(() async {
    await FlutterBugfender.init("YOUR_APP_KEY",
        enableCrashReporting: true, // these are optional, but recommended
        enableUIEventLogging: true,
        enableAndroidLogcatLogging: true);
    FlutterBugfender.log("hello world!");
    runApp(new MyApp());
  });
}

Just remember to import the package, and you’re essentially set! This simple setup means your application is now using robust real-time logging and crash analytics.

The tool offers a comprehensive suite of logging options, allowing for highly granular error tracking (as per their documentation):

// Comprehensive logging capabilities
FlutterBugfender.log("Working fine!");
FlutterBugfender.fatal("Fatal sent!");
FlutterBugfender.error("Error sent!");
FlutterBugfender.warn("Warning sent!");
FlutterBugfender.info("Info sent!");
FlutterBugfender.debug("Debug sent!");
FlutterBugfender.trace("Trace sent!");
FlutterBugfender.sendLog(
 line: 42,
 method: "someMethod()",
 file:"someFile",
 level: LogLevel.info,
 tag: "Custom tag",
 text: "This is a custom log"
);
// Device-specific context
FlutterBugfender.setDeviceString("user.email", "example@example.com");
FlutterBugfender.setDeviceInt("user.id", 32);
FlutterBugfender.setDeviceFloat("user.pi", 3.14);
FlutterBugfender.setDeviceBool("user.enabled", true);
FlutterBugfender.removeDeviceKey("user.pi");
// Advanced reporting
FlutterBugfender.sendCrash("Test Crash", "Stacktrace here!");
FlutterBugfender.sendIssue("Test Issue", "Issue value goes here!");
FlutterBugfender.sendUserFeedback("Test user feedback", "User feedback details here!");
// Control and utility
FlutterBugfender.setForceEnabled(true);
FlutterBugfender.forceSendOnce();
FlutterBugfender.getDeviceUri());
FlutterBugfender.getSessionUri());
FlutterBugfender.getUserFeedback()); // Show a screen which asks for feedback

For me, the most crucial functions that I constantly rely on are:

These functions are invaluable for quickly printing logs, sending detailed crash data to the dashboard, and comprehensively recording all errors occurring within the application. And the dashboard? It’s simply good. Clean, intuitive, and it provides all the contextual information a developer needs to make informed decisions. It gives you the precise stats and actionable insights so you can jump straight into fixing bugs, instead of wasting precious time trying to decipher what a user might have been doing.

While there are many other features to explore, I haven’t delved into them all just yet. But give me time! For now, I highly recommend exploring this incredibly useful, and yes, free, tool for your mobile app debugging needs.

Axiom: Taming the Web Development Wild West (Especially Next.js!)

Now, let’s pivot to the web technology side of things. Yes, I know Flutter for Web exists, and sure, you could technically deploy BugFender there. But while Flutter is a powerhouse for mobile, I find its web output, while improving, still sometimes lacks the nuanced polish and native feel I demand for a truly stellar website.

For my web application development, I’m firmly in the React camp. For those quick, snappy demos and prototypes, Vite is my go-to for its blazing-fast development experience. But when things get serious – think complex blog posts with dynamic content, cool animations, intricate UI changes, or just about anything else that pushes the boundaries – that’s when Next.js truly shines. Its serverless capabilities give me the power to pull off some genuinely crazy, innovative stuff.

A quick heads-up, though: with great power comes great complexity. Next.js can get very intricate very fast, and if not architected correctly, it can become a nightmare. Trust me, I’ve got the scars to prove it! If you’re curious about the ‘trouble’ I got myself into with a scaling cache nightmare, you can dive into that saga in my previous blog post: how I get into a truble.

Enter Axiom: Your Full-Stack Web Logging Champion

So, after all those lessons, when it came to logging and sending data from my Next.js application, I landed on Axiom. For real-time logs and full-stack error tracking, it’s an absolute game-changer. Seriously, it’s incredibly easy to set up and immediately starts giving you valuable insights. The beauty of it is that once imported, it effectively blankets your entire site, capturing all the activity you need.

Setting it up is surprisingly straightforward. First, install the necessary packages:

npm install --save @axiomhq/js @axiomhq/logging @axiomhq/nextjs @axiomhq/react

Next, create a simple axiom.ts file anywhere in your codebase (I usually put it in a lib or config folder) with your client initialization:

// lib/axiom/axiom.ts (or wherever you prefer)
import { Axiom } from '@axiomhq/js';

const axiomClient = new Axiom({
  token: process.env.NEXT_PUBLIC_AXIOM_TOKEN!,
});

export default axiomClient;

Then, for robust server-side logging in Next.js – which is crucial because Next.js handles both client and server code – create another file, say server.ts, and paste this configuration:

// lib/axiom/server.ts
import axiomClient from '@/lib/axiom/axiom'; // Adjust path if needed
import { Logger, AxiomJSTransport } from '@axiomhq/logging';
import { createAxiomRouteHandler, nextJsFormatters } from '@axiomhq/nextjs';

export const logger = new Logger({
  transports: [
    new AxiomJSTransport({ axiom: axiomClient, dataset: process.env.NEXT_PUBLIC_AXIOM_DATASET! }),
  ],
  formatters: nextJsFormatters,
});

export const withAxiom = createAxiomRouteHandler(logger);

Now, you can simply call these logger instances for your frontend logging and backend logging, and you’re good to go! Remember, Next.js isn’t just a simple client-side application like a basic Vite React app; it cleverly blends server-side rendering (SSR), static site generation (SSG), and client-side execution. This dual nature means you need distinct, yet integrated, ways to log activity from both parts of your application.

A Quick Heads-Up: My code snippets here are based on my current setup, but the world of web development best practices and SDKs evolves rapidly. Always consult the official Axiom Next.js documentation for the most up-to-date and accurate setup instructions for your project.

Once everything’s wired up in your code, configuring the Axiom dashboard is surprisingly intuitive. You select your data source, deploy your app, and it often auto-detects, streaming all your activity and error analytics directly to a centralized place.

For context, this is what my Axiom dashboard looks like, monitoring my Next.js backend with Convex:

Axiom Dashboard for Next.js Backend Convex

This is where Axiom truly shines! You can use it across your entire full-stack application, with independent logging for both your frontend and backend. Everything gets logged, from general activity to critical crashes. It’s simple, clean, and makes setup a breeze.

What I particularly love is the flexibility: if your cloud provider supports Axiom integration, you can often consolidate all your default logs directly into Axiom. The ability to see everythingdistributed tracing, API logging, user-triggered events – in a single, unified place is just awesome for production debugging and improving my overall developer workflow. I genuinely love this tool!

Why Axiom Over BugFender for Next.js?

It’s important to clarify one point: while BugFender is fantastic for client-side application logging (and works great for React apps built with Vite, which are typically single-page applications), it doesn’t have robust support for Next.js’s server-side aspects. I’ve tried this before while drafting this very post; BugFender’s focus on the client-side would lead to significant gaps in a Next.js environment, where so much critical logic often runs on the server. Next.js is fundamentally different from a purely client-side SPA, and Axiom fills that server-side logging void perfectly.

Your Debugging Superpower: From “YOLO” to “I Know!”

So, there you have it. My journey from the wild, wild west of “YOLO” deployments, praying nothing broke in production, to a far more serene and informed state. We’ve all been there, pushing code and then bracing ourselves for those inexplicable user-generated errors. But with robust error logging and crash reporting in place, those days are largely behind me.

Whether it’s the intuitive insights of BugFender for my Flutter mobile applications or the comprehensive full-stack logging Axiom provides for my Next.js web projects, these tools have truly transformed my developer workflow. They aren’t just about catching bugs; they’re about understanding the nuanced dance between your code and your users, pinpointing those elusive edge cases, and ultimately, building more resilient and user-friendly applications.

Remember that vow I made at the start? “I never make the same mistake today.” With these tools, that mantra isn’t just a hopeful whisper; it’s a tangible reality. They empower you to stop playing detective in the dark and start making data-driven decisions. You gain a clearer picture of application performance, faster debugging cycles, and more time focusing on what we love most: building awesome features.

So, if you’re a solo dev tired of guesswork, take my advice. Dive into BugFender for your mobile apps and explore Axiom for your web projects. They offer generous free tiers and, more importantly, they offer sanity. Your future self, your codebase, and most importantly, your users, will absolutely thank you. Go forth, log wisely, and build something incredible!

💬 Join the Discussion

Share your thoughts and engage with the community

Loading comments...