Docs Pricing Blog

Breadcrumbs in PHP

Breadcrumbs

Continuing on our JavaScript and Python breadcrumb announcements, we’re happy announce native support in our PHP SDK.

What are Breadcrumbs?

Breadcrumbs are a trail of events that occurred in your application leading up to the primary error. They can be as simple as generic logging messages, or they can contain rich metadata about the state of your application: network requests, database queries, UI events, navigation changes, or even earlier occurring errors.

Breadcrumbs can be incredibly helpful in reproducing the steps that led to the error you’re debugging. We’ve been dogfooding them here at Sentry for some time, and they add critical context to errors that a stack trace alone can’t provide. Once you use them, you won’t go back.

Getting Started

Start by ensuring you’re running a recent verison of our PHP SDK (sentry/sentry on composer).

From there you can capture breadcrumbs by attaching them to the Sentry instance:

<?php

$sentry = new \Raven_Client(...);

$sentry->breadcrumbs->record(array(
    'data' => array(
        'endpoint' => $rpc->endpoint,
    ),
    'category' => 'rpc',
));

You can also use standard log-style annotations:

<?php

$sentry->breadcrumbs->record(array(
    'message' => 'request to ' . $rpc->endpoint,
    'category' => 'rpc',
));

Automated Instrumentation

Automated instrumention in PHP is fairly limited today. We currently support integration with Monolog and the native error_reporting. The error reporting hook will get installed by default, but if you’re using Monolog you just need to register another handler:

<?php

$handler = new \Raven_Breadcrumbs_MonologHandler($sentry);

$monolog->pushHandler($handler);

Note: if you’re using multiple logger instances, you’ll need to bind to each one individually.

Looking Forward

Breadcrumbs for PHP are still in the early phase, but we think there’s a lot of opportunity to integrate them into your application. Prime targets would be areas where you make RPC or HTTP calls. If you have feedback let us know by opening an issue on our public GitHub repository, say hello on IRC, or ping the Sentry Twitter account.

Sentry + Linode <3 PyCon

We’re teaming up with Linode to kick off PyCon in style! Join us for drinks and games at Ground Kontrol on Monday night.

When

Monday, May 30th 7:30 - 9:30 PM

Where

Ground Kontrol Classic Arcade, 511 Northwest Couch St.

Sentry with Bitbucket Pipelines

Sentry with Bitbucket Pipelines

After pushing out a new release, there’s always one looming question: “Did the push break anything?” We built release tracking into Sentry to answer this question and make Sentry an invaluable tool in deployment, especially for teams practicing continuous deployment.

With the launch of Bitbucket’s continuous delivery service, Bitbucket Pipelines, Atlassian has been working with Sentry to create a Bitbucket Pipelines integration. We worked with them in the past to build Sentry’s JIRA Software and HipChat Connect integrations, so simplifying deployment for Bitbucket users was a logical choice.

When a new error crops up in production, Sentry enables developers to pinpoint the guilty release. Once a fix is merged in, developers then mark an issue as resolved to silence alerts until the next release is out. Our Bitbucket Pipelines script notifies Sentry as new releases are pushed out. Apps with minified Javascript and offline source maps can use Pipelines to upload source maps, enabling legible, decompressed stack traces. Check out how you can integrate Sentry and Bitbucket here.

Have questions or feature requests? We’d love to hear from you. Please file an issue in the public issue tracker, or email us privately at hello@getsentry.com.

Breadcrumbs in Python

Breadcrumbs

A couple weeks back we announced Breadcrumbs, with full support in our JavaScript SDK, and the in-progress implementations for other platforms. Today we’re going to focus on Breadcrumbs in our Python SDK, what they capture, and how you get started using them.

What are Breadcrumbs?

Breadcrumbs are a trail of events that occurred in your application leading up to the primary error. They can be as simple as generic logging messages, or they can contain rich metadata about the state of your application: network requests, database queries, UI events, navigation changes, or even earlier occurring errors.

Breadcrumbs can be incredibly helpful in reproducing the steps that led to the error you’re debugging. We’ve been dogfooding them here at Sentry for some time, and they add critical context to errors that a stack trace alone can’t provide. Once you use them, you won’t go back.

Getting Started

You’ll want to ensure you’re running the latest version of our Python SDK (raven-python), but anything newer than 5.18 will have some level of support.

There’s a variety of events that you might want to capture as part of breadcrumbs, but a common example might be internal RPC calls:

from raven import breadcrumbs

breadcrumbs.record(
    data={
        'endpoint': rpc.endpoint,
    }
    category='rpc',
)

You can also pass simple string-based annotations, rather than a JSON blob:

from raven import breadcrumbs

breadcrumbs.record(
    message='request to {}'.format(rpc.endpoint),
    category='rpc',
)

Automated Instrumentation

Currently Sentry will automatically capture a few things for you:

  • HTTP requests made using httplib, urlib, or requests
  • Logging entries from stdlib’s logging package
  • SQL queries from the Django ORM

One common pattern we’ve already seen is that some loggers might be noisy. Because we instrument and capture all log entries, it often makes sense to filter things out. For example, we might want to disable oauthlib because it outputs a lot of noise, and potentially sensitive data:

from raven import breadcrumbs

breadcrumbs.ignore_logger('oauthlib')

We’ll be evolving this as time goes on, so be on the lookout for more automated instrumentation in the future.

Looking Forward

We’re always super excited to bring new levels of data to Sentry. Python has been one of our best supported platforms, and we think Breadcrumbs bring it to an entirely new level. That said we’re still early in the design, and are rapidly iterating on the concept. If you have feedback let us know by opening an issue on our public GitHub repository, say hello on IRC, or ping the Sentry Twitter account.

What the heck is "Script error"?

Script Error

If you’ve done any work with the JavaScript onerror event before, you’ve probably come across the following:

"Script error."

“Script error” is what browsers send to the onerror callback when an error originates from a JavaScript file served from a different origin (different domain, port, or protocol). It’s painful because even though there’s an error occurring, you don’t know what the error is, nor from which code it’s originating. And that’s the whole purpose of window.onerror – getting insight into uncaught errors in your application.

The cause: cross-origin scripts

To better understand what’s going on, consider the following example HTML document, hypothetically served from http://example.com/test:

<!doctype html>
<html>
<head>
  <title>example.com/test</title>
</head>
<body>
  <script src="http://another-domain.com/app.js"></script>
  <script>
  window.onerror = function (message, url, line, column, error) {
    console.log(message, url, line, column, error);
  }
  foo(); // call function declared in app.js
  </script>
</body>
</html>

Here’s the contents of http://another-domain.com/app.js. It declares a single function, foo , whose invocation will always throw a ReferenceError.

// another-domain.com/app.js
function foo() {
  bar(); // ReferenceError: bar is not a function
}

When this document is loaded in the browser and JavaScript is executed, the following is output to the console (logged via the window.onerror callback):

"Script error.", "", 0, 0, undefined

This isn’t a bug – browsers intentionally hide errors originating from script files from different origins for security reasons. It’s to avoid a script unintentionally leaking potentially sensitive information to an onerror callback that it doesn’t control. For this reason, browsers only give window.onerror insight into errors originating from the same domain. All we know is that an error occurred – nothing else!

I’m not a bad person, really!

Despite browsers’ good intentions, there are some really good reasons why you want insight into errors thrown from scripts served from different origins:

  1. Your application JavaScript files are served from a different hostname, e.g. static.getsentry.com/app.js.
  2. You are using libraries served from a community CDN, like cdnjs or Google’s Hosted Libraries.
  3. You’re working with a commercial 3rd-party JavaScript library, that is only served from external servers.

But don’t worry – getting insight into errors served by these files just needs a few simple tweaks.

The fix: CORS attributes and headers

In order to get visibility into errors thrown from scripts originating from different origins, you must do two things.

1) Add a crossorigin=”anonymous” script attribute

<script src="http://another-domain.com/app.js" crossorigin="anonymous"></script>

This tells the browser that the the target file should be fetched “anonymously”. This means that no potentially user-identifying information like cookies or HTTP credentials will be transmitted by the browser to the server when requesting this file.

2) Add a Cross Origin HTTP header

Access-Control-Allow-Origin: *

CORS is short for “Cross Origin Resource Sharing”, and it’s a set of APIs (mostly HTTP headers) that dictate how files ought to be downloaded and served across origins.

By setting Access-Control-Allow-Origin: *, the server is indicating to browsers that any origin can fetch this file. Alternatively, you can restrict it to only a known origin you control, e.g.

Access-Control-Allow-Origin: https://www.example.com

Note: most community CDNs properly set an Access-Control-Allow-Origin header.

$ curl --head https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.js | \
    grep -i "access-control-allow-origin"

Access-Control-Allow-Origin: *

Once both of these steps have been made, any errors triggered by this script will report to window.onerror just like any regular same-domain script. So instead of “Script error”, the onerror example from the beginning will yield:

"ReferenceError: bar is not defined", "http://another-domain.com/app.js", 2, 1, [Object Error]

Boom! You’re done – ”Script error” will plague you and your team no more.

An alternative solution: try/catch

Sometimes, we’re not always in a position to be able to adjust the HTTP headers of scripts our web application is consuming. In those situations, there is an alternative approach: using try/catch.

Consider the original example again, this time with try/catch:

<!-- note: crossorigin="anonymous" intentionally absent -->
<script src="http://another-domain.com/app.js"></script>
<script>
window.onerror = function (message, url, line, column, error) {
  console.log(message, url, line, column, error);
}

try {
  foo(); // call function declared in app.js
} catch (e) {
  console.log(e);
  throw e; // intentionally re-throw (caught by window.onerror)
}
</script>

For posterity, some-domain.com/app.js once again looks like this:

// another-domain.com/app.js
function foo() {
  bar(); // ReferenceError: bar is not a function
}

Running the example HTML will output the following 2 entries to the console:

=> ReferenceError: bar is not defined
     at foo (http://another-domain.com/b.js:2:3)
     at http://example.com/test/:15:3

=> "Script error.", "", 0, 0, undefined

The first console statement – from try/catch – managed to get an error object complete with type, message, and stack trace, including file names and line numbers. The second console statement from window.onerror, once again, can only output “Script error.”

Now, does this mean you need to try/catch all of your code? Probably not – if you can easily change your HTML and specify CORS headers on your CDNs, it is preferable to do so and stick to window.onerror.

But, if you don’t control those resources, using try/catch to wrap 3rd-party code is a surefire (albeit tedious) way to get insight into errors thrown by cross-origin scripts.

Note: by default, Raven.js – Sentry’s JavaScript SDK – carefully instruments built-in methods to try to automatically wrap your code in try/catch blocks. It does this to attempt to capture error messages and stack traces from all your scripts, regardless of which origin they’re served from. It’s still recommended to set CORS attributes and headers if possible.


If this blog post helped you out, and you’d like to learn more about window.onerror, you should also check out our other blog post: Capture and report JavaScript errors with window.onerror.