Environment Variables

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

One big part of Symfony's configuration system that we have not talked about yet is: environment variables. To show them off, let's implement a real feature in our app.

Go to sentry.io. If you've never used Sentry before, it's a cloud-based error monitoring tool: it's a really great way to track and debug errors on production. Not that those ever happen. Ahem. They also have excellent integration with Symfony.

If you don't already have an account, sign up - it's free. Once you do, you'll end up on a "Getting Started" page that looks something like this. I'll select Symfony from the list. Ok: it wants us to install some sentry/sentry-symfony package.

Installing sentry/sentry-symfony & Contrib Recipes

Before you do, make sure you've committed all your changes to Git.

git add .
git commit -m "your commit message here..."

I committed before hitting record, so I'm good to go. I like to do this before installing a new package so I can see what its recipe does.

Back on the docs, copy the composer require line, move over, and paste:

composer require sentry/sentry-symfony

It's downloading some packages and... interesting! It says:

The package for this recipe comes from the contrib repository, which is open to community contributions. Do you want to execute this recipe?

There are actually two places that recipes come from. The first is the main, official recipe repository, which is heavily-guarded for quality. Every recipe we've installed so far has been from that. The second is a "contrib" repository. That repository is less guarded for quality to make it easier for recipes from the community to be added, though the recipe still requires approval from a core Symfony member or an author of the package itself. The point is: if you're cautious, you can check a contrib recipe before you install it.

I'm going to say yes permanently by saying p. Ok, what did the recipe do? Run:

git status

It modified the normal stuff - like composer.json, composer.lock, symfony.lock and config/bundles.php because this package contains a bundle: SentryBundle:

... lines 1 - 2
return [
... lines 4 - 12
Sentry\SentryBundle\SentryBundle::class => ['all' => true],
];

Hello Environment Variables & .env

The recipe also updated .env and added a new config file. Let's go see what's going on.

First, open up .env and scroll to the bottom. Woh! This has a new section that sets an environment variable called SENTRY_DSN:

26 lines .env
... lines 1 - 22
###> sentry/sentry-symfony ###
SENTRY_DSN=
###

Environment variables are not a Symfony or PHP concept: they're values that you can pass to any process on your computer to configure that process's behavior. Symfony supports reading environment variables, which we'll see in a minute. But setting them can be a pain: it's different for every operating system. For that reason, when Symfony loads, it reads this file and sets anything here as an environment variable for you.

Reading Environment Variables with %env()%

So... if we're setting a SENTRY_DSN environment variable... what's using that? Go into config/packages/ and open the shiny new sentry.yaml file, which, not surprisingly, configures the new SentryBundle. Check this out: it has a dsn key set to a very strange value: %env(SENTRY_DSN)%:

sentry:
dsn: '%env(SENTRY_DSN)%'

This... kind of looks like a parameter, right? It has percent signs on both sides, just like how, in cache.yaml, we referenced the cache_adapter parameter with %cache_adapter%:

framework:
cache:
... lines 3 - 14
app: '%cache_adapter%'
... lines 16 - 20

And... it is sort of a parameter, but with a special super-power: when you surround something by %env()%, it tells Symfony to read the SENTRY_DSN environment value.

Why Environment Variables?

So... why are we setting an environment variable in .env and then reading it here? Well, the SENTRY_DSN string will be a sensitive value: if someone got access to it, they would be able to send information to our Sentry account.

Look back at the setup guide and skip down to the DSN part. Technically, we could copy this value and paste it right into sentry.yaml. The problem is that this file will be committed to git... and it's generally a bad idea to commit sensitive values to your repository.

To avoid that, this bundle correctly recommended that we use an environment variable: we'll store the environment variable somewhere else, then read it here.

.env & .env.local

And, as we talked about, it is possible to set a real SENTRY_DSN environment variable on your system. But... since that's a pain, Symfony allows us to instead define any environment variables we need in .env if we want to... which we will.

But... this .env file is also committed to the repository: you can see that in the terminal if you run:

git status

So if we pasted the SENTRY_DSN value here, we would have the same problem: the sensitive value would be committed to the repository.

Here's the deal: the .env file is meant to store non-sensitive default values for your environment variables - usually values that are good for local development. This works because after Symfony loads .env, it looks for another file called .env.local.

We don't have that yet, so let's create it: .env.local.

Anything you put in this file will override the values in .env. Let's add our real value here: SENTRY_DSN= then paste:

2 lines .env.local
SENTRY_DSN=https://7f45741877f3498eab0ae2bee6463d57@o372370.ingest.sentry.io/5186941

Perfect! In .env, we set SENTRY_DSN to a non-sensitive default:

26 lines .env
... lines 1 - 22
###> sentry/sentry-symfony ###
SENTRY_DSN=
###

in this case empty quotes means "don't send data to Sentry": and in .env.local we override that to the real value:

2 lines .env.local
SENTRY_DSN=https://7f45741877f3498eab0ae2bee6463d57@o372370.ingest.sentry.io/5186941

If you're confused why this is better, there's one more thing I need to tell you. Open up .gitignore: the .env.local file is ignored from Git:

18 lines .gitignore
... line 1
###> symfony/framework-bundle ###
/.env.local
... lines 4 - 18

Check it out: at your terminal, run:

git status

It does not see .env.local: our sensitive value will not be committed. To see the final environment variable values, we can run:

php bin/console about

This gives us a bunch of info about our app including, at the bottom, a list of the environment variables being loaded from the .env files. It's working perfectly.

Seeing it Work!

So let's... see if Sentry works! In the show() controller, throw a very realistic new \Exception():

bad stuff happened!

... lines 1 - 11
class QuestionController extends AbstractController
{
... lines 14 - 41
public function show($slug, MarkdownHelper $markdownHelper)
{
if ($this->isDebug) {
$this->logger->info('We are in debug mode!');
}
throw new \Exception('bad stuff happened!');
... lines 48 - 62
}
}

When we installed SentryBundle, it did add some services to the container. But the main purpose of those services isn't for us to interact with them directly: it's for them to hook into Symfony. The bundle's services are set up to listen for errors and send them to Sentry.

So all we need to do is... refresh! There's our error. Back on Sentry, I should be able to go to sentry.io and... yep! It takes me over to the SymfonyCasts issues and we have a new entry: Exception: bad stuff happened!

Next, how do you handle setting environment variables when you deploy? It's time to check out a cool new system called the secrets vault.

Leave a comment!

  • 2020-06-29 weaverryan

    Hey Ian!

    Hmm. Now 2 people have had an issue - let's see if we can figure this out :). The best way to check your config is to directly dump the Sentry "Hub" object - check out what we do in the next chapter - https://symfonycasts.com/sc... - that will tell you *exactly* what config Sentry is seeing. You should be able to see if it's misconfigured. If you see a NullTransport instead of an HttpTransport, then (for some reason) your SENTRY_DSN is set to an empty string (which disables sentry).

    Unfortunately, Sentry can be a bit hard to debug because, if there is any error sending info to Sentry, Sentry hides the error. It does this for good reason: if Sentry fails for some reason on production, it's probably better for it to *not* take the whole site down. But it *can* make things tricky. If you think that your config is correct but you *still* don't see anything, you could also open this core class and add a dd($exception) right before this return null: https://github.com/getsentr... - but to get this to work, you will also need to set $this->delaySendingUntilShutdown = false;right before this line: https://github.com/getsentr...

    If you do those 2 things, and there's a failure, you should see it. Unfortunately, while Sentry itself has support for logging (you can see logger code in there), the bundle doesn't integrate with Symfony well enough to make that a *real* logger - it is a NullLogger... which means it logs nowhere.

    Let me know if that helps! I'm happy to keep debugging with you :).

    Cheers!

  • 2020-06-27 Ian

    I also cannot get the exceptions to send to Sentry. I cleared the cache and double-checked the credentials.

    The errors I see in the console seem to all be related to the exception I am throwing. Is there a way to verify that the bundle is sending out the error to Sentry?

  • 2020-06-22 Victor Bocharsky

    Hey Michał,

    Hm, do you have any errors? Please, double check your logs. Also, I'd recommend you to clear the cache and try again. And make sure that your Sentry credentials are matched the ones for your Sentry account.

    Cheers!

  • 2020-06-19 Michał Śnieżko

    Hello, the Sentry is not working for me - I've done everything as you did in the video but my Sentry issues page is empty.