In this chapter, we're going to add production-grade error tracking and observability to our app. Basically, we're going to transform silent failures, which will happen all across the app, into some actionable insights to something you can fix and something you are aware of when it happens. This chapter will aim to show you the difference between flying blind and having full observability in production. So let's get started by demonstrating some types of errors that can happen within our app. Make sure you have npm run dev running and make sure you have your ingest CLI running as well.
So now I'm going to go ahead and make sure my app is active on the localhost 3000 forward slash demo. Basically where we tested our blocking and background ones. And now I'm going to prepare my ngis developer server here as well simply so I'm ready to observe what happens there as well. I'm going to go inside of my app folder, demo page .tsx and now I'm going to go ahead and prepare a scenario of when we have a client error. So what is a client error?
That's something that happens well on the client. That would be right here. Right? In some useClient component. This can be various reasons.
It can be an invalid access in an object, It can be an invalid function, an invalid type, many things. But in our demo scenario we can just do this. Handle client error and let's just do throw new error. Client error, something went wrong in the browser. So that's the first scenario we need to have.
Then let's prepare a second scenario, an API error. So we are going to purposely fetch an API endpoint which will throw back an error. And then we're going to have a third scenario. Ingest error. Basically, this will successfully make a fetch request, but the background job itself will throw an error.
So that will be interesting to observe. So let's go ahead and first test the client error. This is the one that should be very easy to do. So I'm just going to go ahead here and I will add a new button. I'm going to give it an on click variant of destructive and I will add a label client error.
So we should now see client error button right here. What happens when I click? Well, as you can see, we have an error. That is very expected, right? But the problem is only the user and well us now in development mode are aware that something actually went wrong in the browser.
We actually have no idea what went wrong. We don't know what user was trying to do. And worst of all, we genuinely don't know that a user got an error which is unacceptable in production. We need to be aware of every single error that happens in our app on the client side, on the server side, or in a background job. So now let's go ahead and copy this and demonstrate a server error or an API error so handle API error.
In order to test this out we have to create the demo error which is very simple. So inside of an API folder, demo, let's create a new folder called error and inside of there route.ts and let's simply add an invalid post method. Just a simple error. Something went wrong on the server. So I'm gonna go ahead and call this API error.
So even worse experience here we don't even see anything happen. That's because this is a server error and we can only figure that out if we go inside of our server logs right here. API error, something went wrong on the server. So this is even harder to observe, right? Even the user is not really aware of what just happened.
And you can guess that the third one is even worse than that. So let's just go ahead and quickly prepare that. So I'm just going to copy this and I will call this ingest error handle ingest error and let's go ahead and just create a very simple route. So inside of this demo I will create a new folder and I will call it ingest-error. I will create a new route .ts inside and I will paste the following.
I'm going to import ingest client. I'm going to export an asynchronous post method. And all I'm going to do is I'm going to trigger a background job with the name of the demo forward slash error. And then I'm going to send a what is a success status of well response dot JSON just status started. Right.
This is what this would be considered a successful API call. Right. So I'm going to go ahead and now create that background job. So let's quickly go inside of source ingest functions dot ts. And just as we have a demo generate, so the same thing, right?
At the bottom here, I'm going to add demo error, ingest create function. First argument is ID, second argument is very important, demo forward slash error, which should match exactly what we trigger inside of the API demo ingest error route and we only have one step which will run fail and it will just throw an error background job failed. In order to make this function we also need to go instead of API ingest route.ts and add a demo error from that very same file so we now register it. So now let's go ahead and click on ingest error. So even less information happening on the client but on the actual ingest development server we can see that this step is struggling.
So something is going on. This step is simply keeps failing. You can see that Ingest thankfully will attempt to retry this step over and over again with polling increasing each time. So it doesn't spam or reach a rate limit if it were a third-party service but still we know that we hard-coded this to an error. So those are three serious scenarios of which we have absolutely no idea that they're happening.
How do we improve that? One of the most popular tools for resolving this issue is Sentry. And using the link on the screen, you can get Sentry Team for free for 3 months, as well as 150, 000 errors completely for free. Let's go ahead and create an account and then let's create a project. Once you are in your dashboard you will see a prompt to create a project.
I'm going to select Next.js as that is our framework. And I'm not going to modify the defaults at all, let's just modify our project's flag to be Polaris and click create project. And then in here we're going to get an install script. So we can just copy this. I'm going to shut down all of my terminals for now.
We're going to get them up and running later. So this is the command. Npx sentry-visit-latest and I'm going to use next.js with a sass flag, organization name This is my organization name John Joe. Yours will be different and a project name. I chose Polaris.
You chose whatever you entered or you just left it at default, it doesn't matter. Let's just go ahead and run this within our project here. So in here it's telling us that we have some uncommitted or untracked files in our repo and that's basically a demonstration of all of these errors. So it's warning us that it might overwrite some files but this is perfectly fine because these are demo files anyway. So yes, we are going to continue anyway.
Let's confirm that. After this, it's going to open a browser and it's going to connect. So as you can see, we now have login complete. You can go back to your terminal and now it is installing Sentry using npm. And after this, we're going to answer some questions.
So for the first option, if you want to route sentry requests in the browser through the Next.js server to avoid ad blockers, basically giving you a better overview of what's going on with your app, you can select yes. Same thing for tracing, same thing for session replay, which is honestly magic-like reproduction of what happened. Logs will be very important. This will allow us to create a structured trace of what actually led up to the event. And if you want to, well I would recommend yes, select yes for this too, which is basically an example error page.
And you can select yes for using a CI-CD tool because that will give us the sentry auth token. So copy the sentry auth token, make sure you copy it in full and immediately store it in your .environment just make sure you don't commit .environment which should be the default behavior. So I'm going to add Sentry and just paste let me go ahead and copy this code and paste it here there we go and we can just say yes continue And I will select no for ProjectScope MCP server simply because we are not going to be doing any AI coding things in this project. Well, besides building an AI editor. Great!
So I'm going to go ahead now and run npm run dev again. And the first thing I'm going to do is I'm going to visit the page that they have created for us. So sentry example page. You can visit this page at localhost 3000 sentry-example-page. So I'm going to copy that and paste it here and we should be able to visit Sentry's pre-made page here.
Ignore the purple around our button, that is because this page has some custom styles. It's just for demo. And in here they have prepared a sample error similar to how we have prepared it, but you can see a crucial difference. This time the error was logged to Sentry. So now let's go ahead and let's click on our issues right here.
So 15 seconds ago you can see some errors happening. We can see the Sentry example API error as well as Sentry example frontend error. So from now on we can actually see why our errors are happening And I want to show you something very impressive. So I'm going to expand my screen here Go inside of sentry example front-end error. I'm pretty sure this is where it's happening and look at this This is called a session replay.
This is somehow rebuilding what the user was doing in your UI before the error occurred. So you can see this is representing, you can see this right, it's representing this very screen. It's obviously saving on the resources or maybe things it doesn't have access to, but you can see we very clearly clicked on this button and that is what triggered the error. Right, So this would help you a lot if you had some unknown error or if the user doesn't know or didn't explain how it happened, you would have almost like a camera of what the user was doing at the time. And using their breadcrumbs, you can go so much in depth.
Look at this about every single thing that happened here. Right. This is an ideal way of actually fixing a bug. This is insane. But we are just scratching the surface.
So now I'm going to go to my demo page, right? And in here I'm going to try calling the client error. So this is the problem we had first time, right? User gets an error and let me just show you in case you're lost we are on forward slash demo because I know this is very small font so that's why I'm showing you. So this time we should get an improvement over that.
There we go, Another error successfully tracked. Client error. Something went wrong in the browser which is exactly what we log in our code. So exactly what happened. This is the error that we just triggered.
You can see the exact URL where it happens, forward slash demo. You can see my Chrome version, my Mac OS version, a bunch of things right here and as always You can see the trace preview, you can see the breadth grumps. Let's go ahead and go even further. So now I'm going to try triggering the API error again. Right now we can only see that here, right?
Something went wrong on the server. But let's give it a second. Let's maybe refresh and it's now going to appear here. There we go. API error.
Something went wrong on the server. So forward slash API demo error. You can now see and observe every single error happening in your project. The only thing that I believe isn't added by default is the ingest error. And in fact, we also need to have our ingest development server running to test this out.
I'm pretty sure that when I click on ingest error right here, it is going to fail, but I don't think this will observe it by default. Instead, there is something called a middleware which we can use to add Sentry to. This is what I was talking about. So Ingest has a Sentry middleware. And this is the cool thing about using Sentry.
99% of the tools that we will be using have full sentry support. So that is amazing. That's how good of a tool this is. So let's start by installing the Ingest Middleware Sentry here. I'm just going to go ahead and install that.
So npm install Ingest Middleware Sentry. This is the version at the time of me making this video. So 0.1.3 and now I'm going to go ahead and copy this import and I will go inside of my OK. I can remove this comment here. Let me copy the import again.
And I will go inside of my ingestclient.ts and I will add the import. There we go. And then I'm just going to add it to my middlewares here. So currently I have no middlewares at all. Basically this will now intercept all of our background jobs and we will be able to see what's going on between them.
You can see some rules here So in case it's not working or something is erring, it's probably a version mismatch. So I would suggest that you can basically just Google Sentry Middleware Ingest and you can find this page right here. So let's go ahead now and I will just restart my npm run dev and my ingest server. I will refresh both of them and I'm going to refresh my feed here in the sentry project And now I'm going to trigger that ingest error once again. And let's see if something else is happening now in the feed here.
So I'm going to refresh the feed. And there we go. We can now track all the errors happening in our background jobs too. And keep in mind, this can be so many things, right? So let's go ahead and take a look at something.
Inside of our app folder demo, we had some... Actually we can go directly inside of ingest functions so take a look at all the steps that are possible we can fail with URL extraction right? Maybe the prompt isn't a string right? This step can fail And usually we wouldn't even know that it failed. We probably think that user cannot enter anything other than a string.
But now, if that happens, we will be aware of that. Right? We are going to know exactly what happened. Look at this. I can see exactly where it appeared, exactly in what step.
So this is an extremely, extremely useful thing. It can happen in some third-party providers like Firecrawl, right? What if that fails? We are now aware of all of those amazing things. That is the power of Sentry.
But we are not done yet. We can improve our observability and monitoring even further. When building an app like ours, it is also crucial to know how much tokens we are spending. That simply cannot go unnoticed. And Sentry has a solution for that as well.
In fact, it has a solution for the very provider of AI we are using, Vercel AI SDK. So following this documentation, we're now going to enable that so you will see exactly how well Sentry understands what's going on with our app with just a few line changes. So first things first, let's go inside of Sentry.edge.config So I'm gonna find Sentry.edge right here, and I'm going to find my Century.Init so here it is my DSN, my traces sample rate and my enable logs which is sent to true. So at the bottom here I'm going to add integrations. I'm going to open the array and I'm going to add century dot Vercell AI integration.
So make sure you add this to your century dot edge dot config dot TS right at the bottom. This is where I'm going to add that. And now what we have to do is we have to find some places where we are actually calling generate text. So I already know I have one inside of my API demo blocking a route.ts example. Here it is.
I have generate text right here. And then after I do the prompt I'm going to add experimental telemetry. Keep in mind that what's called experimental can sometimes be deprecated right. You can see that we already had a bunch of deprecated things. That usually means that the new name is what was after experimental, right?
So you can see now it's ActiveTools, but before it was ExperimentalActiveTools. I'm just telling you this in case telemetry gets deprecated that probably means you can now just use telemetry but both should actually work. And let's do isEnabled to true, recordInputs true and record outputs to true. And I will copy this and then I'm going to go inside of my Ingest background job. So let me go ahead and find my ingest functions.ts and in here I have another generate text so I'm just going to add the experimental telemetry here then I'm just going to restart my app And I'm going to restart my Ingest developer server.
I'm going to stay on the demo page here and I'm going to go back to the issues here and I'm going to go ahead and just you know start using my blocking. Let's just wait for it to finish. Yeah, this is the example of the blocking one and my background one. So basically we just triggered some generate texts and now we're going to see if we can find some AI telemetry within our Sentry project. And completely accidentally, Sentry actually caught an error in my ingest background job.
That is because I completely forgot but yes this will now fail because remember we now have to pass data and we have to pass prompts inside. That is how our background job now works because we accept a prompt to extract some URLs inside so yours might have failed too and I would be clueless as of why it happened until I looked at the code but this way I can see that it was caught by Sentry So it was so easy to make a mistake on our end. And now, as you can see, it was immediately caught here. And just by seeing this line, I would immediately get reminded, oh, I forgot to pass the prompt, right? I forgot that we modified that from the previous chapters, but lucky for us the blocking one should still be working just fine, right?
That is just a normal generate text. So now find the insights, click on AI and click on agents. In here, as you can see, I can see exactly which LLM calls I did, as well as how many tokens those LLMs actually used. And in here I can click on the exact trace ID. So this is the one API demo blocking.
Let me go ahead and zoom in so you can see post request on API demo blocking. We called generate text and we spent that many tokens and then you can see some more information here and you can see exactly which prompt it was and you can even see the exact response right here and all this information is available So you can see that just by enabling that one small integration, we now have full observability over our AI agents. We can see exactly which ones are being called, which ones are failing, and how many tokens they are using. So all of this is so interconnected when you actually start to research Sentry and how it works. And one last thing I want us to demonstrate here is SentryLogs.
So that is actually already enabled if I take a good look in my Sentry.edge. So enable logs is set to true. So just make sure you have it here as well. But what we can do is something a bit more fun. We can basically explain to our trace logs what the user was trying to do by giving some logging information whenever user clicks a function or whenever we trigger a background job, even though it's very clear from Sentry itself what happened, we can be more descriptive ourselves and do that.
So by using Sentry's new feature called Logs, you can actually create structured information and give you a better understanding of why certain errors have happened. And they're actually super easy to set up. In fact I believe they are already configured here. So make sure that instead of your Sentry config files, both of them, right, I think it's very important that Both of them are identical actually. So yes, make sure both your Sentry server and Sentry Edge have the same information in them.
But this is the one we are focusing on now. Enable logs set to true. And this is what you can basically do now. You can create structured information whenever user is doing something. This is actually a standard practice in production.
So if I go inside of my demo page right here and let's focus on these three functions that I have handle client error API error and ingest error. I'm going to go ahead and for example use the century logger to log that information so import all as century from at century next JS and then I can use the century logger and for example let's do info we can of course do error trace debug warn whatever we want right so for example century logger info and in here I'm just going to say user attempting to click on client function and you can give it even more information. So since we are using the clerk, what we can actually do is we can get the user ID from use user ID. Use, my apologies, use auth. Use auth is a function you can import from clerk, clerk react or clerk next JS.
I think both of them should work just fine. And you can destructure the user ID from here like this. And then you can go ahead and just pass that along here. Right. You can see they actually tell you that you can do it like this.
Basically structured information. So this user attempt to click this button and then you can track along if that user breaks something going forward and you can see that side by side. So I have added this to handle client error, which is used in the client error button. So when I click here, I'm expecting that to be logged. So let me show you where you can find logs, go inside of explore and then go inside of logs right here.
And if you have a prompt here to enable it, you can go ahead and click enable logs at the top. And after a few moments you will see a new log here. User attempting to click on client function. And here is our property, user ID. And here's the best part.
So these logs by itself are okay, But this is where the magic happens. If you actually go and find the irrelevant error, which is something went wrong in the browser, and you scroll down, you can actually see the related log. So you now have more information, right? What user this happened to and what were they doing? Well, user attempting to click on a client function.
That is what was happening. That is the power of structured logs. You can see that they are way more powerful than that actually. You can trace things when starting a database connection and tell exactly what database we are connecting to. We can throw fatal ones if database connection pool is exhausted and you can track exactly at what number of connections that has happened.
So very useful and you can of course automate it and abstract it to a much more useful state than that. But here's another cool thing that they can actually do. They have integrations. And you can, for example, send console log, warn, and error calls all as logs to Sentry. So I'm gonna go ahead and do that.
And then whenever my app or one of my third party libraries throws these errors or logs or you know things like that you have to add that to integration. So right here there we go. Our app will now receive a log one and error one. So I'm going to add that to both here. So I have identical ones and there we go, right?
Just like that, our logs will now be listening to various console traces throughout our app. So that's what was happening here. You can see Ingest is throwing some console warns or errors and I can see that happening in my logs and I can see that happening next to my errors. So you can do so, so much with Sentry and I cannot recommend this product enough. This is the go-to product for error tracking.
They have a super generous free tier and you will get alerted for every single error that happens, which in production is invaluable how much helpful it is. I believe that I have shown you the power and We now learned the difference between going in blind and having some proper observability, production grade observability. So let's take a look at what we did. We've set up Sentry with Next.js wizard, We caught a background job failure with ingest. We figured out how to monitor AI calls and token usage.
And we added structured logs for user interactions. And finally, we even added user context like user ID for even better debugging. So let's go ahead and let's merge all of these changes. We will clean up our extra stuff later but for now we can just do git add and then a dot and then git commit. So I previously made a mistake.
I have a 04 twice. So I have to be careful. This is now chapter six. So I'm going to call this error tracking. And then once I've created this commit, I will do git checkout-b 06 error tracking.
There we go. Git push u origin 06 error tracking. And we can do a quick job of a merge here simply because there is not much to review when it comes to a wizard who added all of the files which was npx sentry every other new file is a file that's going to be deleted, meaning for example all of this was added by Sentry, so no need to review that because we are not going to modify what they have added, right? But these things like an ingest error route and a registration of that, all of that will be removed obviously these are all just demo examples so because of that it makes no sense to waste time let's go ahead and merge this and let's get on to building new features so I'm going to go ahead and merge this pull request Make sure to not delete the branch. So now I have twice the number 04, but we know that this is actually supposed to be 05.
Let's go ahead and git checkout back to the main branch and git pull origin main So we are up to date with our remote merged changes. And to wrap it all up now, I always love to confirm my graph here. Here it is. So yes, 04 appears twice, but you can see logically, chronologically, that is 05. Amazing!
I believe that marks the end of this chapter. Amazing, amazing job and see you in the next one.