You should print your application configuration on startup
Printing your application configuration on startup (while hiding secrets) will help you figure out what’s wrong more quickly.
Background
I went to Slack recently to ask for a good way to print configuration on startup for a FastAPI application that I’m working on. I had been googling for a bit, and found that if I use BaseSettings
from pydantic to hold my configuration, and then use SecretStr
(also from pydantic) to hold my secrets, then I can easily and safely do a:
log.info(f"Application started with this config: {api_config.dict()}")
to print out my configuration on startup. The log output may look something like this:
{“level”: “INFO”, “message”: “Application started with this config: {‘connection_string’: ‘SecretStr(****)’, ‘other_value’: ‘42’}”, “loggerName”: “root”, “processName”: “MainProcess”, “processID”: 24101, “threadName”: “MainThread”, “threadID”: 3621210640, “timestamp”: “2022-10-23T13:30:00.000Z”}
This works well, but went on Slack to ask for tips nevertheless. It never hurts to check if there are other good strategies our there. What met me on Slack though, to my surprise, was a lot of people trying to convince me that I shouldn’t do this. The discussion that followed resulted in this blog post, where I try to summarize the arguments made for and against.
Why you should log application configuration on startup
- You may not always be able to access the runtime environment of your application, but you probably can access the logs.
This can be for many reasons:
Maybe you’re shipping the application you write to customers, for them to run? They likely won’t let you into their production environment, but can gladly send you the application logs if there’s something they need your eyes on. Logging the application configuration on startup will then help you to more easily figure out what’s wrong.
Maybe you’re working in a sensitive environment, such as health of finance. In those fields it’s common to have tightly controlled production environments, where few people have access. In these cases, you may not be able to go to where your application is running to see what the configuration ends up being. But logs are very often extracted and shipped to some central location, and are open for more people to see. Having printed your application configuration will then help you debug what’s wrong more quickly. You won’t need to ask someone with higher privileges than yourself to review the runtime environment. As an example, where I work very few people have access to the Kubernetes production environment, so developers can’t go into the running Kubernetes Pod in production to see what the environment looks like. But they can look at logs, which are available elsewhere.
Maybe you’re debugging something weird that happened two months ago, and you don’t need to see how the application is configured right now, but how it was configured then. Depending on how you manage your runtimes, the description of what that environment looked like two months ago may be available to you in some version-controlled system. It’s probably not, though, but the logs likely are. With the logs in hand, you would be able to see exactly how the application was configured at that point in time.
Counter-arguments made
That still won’t actually protect you if something is misconfigured though.
No it won’t, but it’ll very quickly give you a hint of what was wrong.
I’d expect at as a developer to be able to read what my app is configured with.
You can’t expect that. The production environment may only be accessible to a few people.
Sounds like the “print these specific config values at startup” approach is what you should probably go with
You don’t know what will cause a failure in the future, though. How should you pick which configuration values to print, and which not to print? (You should of course hide sensitive values either way.)
Configuration is mainly used for initializing and using different components.
This is not always true. An application may run for weeks without a specific configuration value being used. And then a very specific request comes in, requiring that value, and then it’s actually used, causing a failure.
If the application didn’t start with the correct configuration, then it should just fail hard and loud.
What if the values are semantically correct, but you’re for example pointing towards the wrong database? I.e. pointing to the staging database instead of the production database. The application wouldn’t fall over then, since the database connection string is indeed working. You are actually connected to a database, which even has the correct schema.
You should have various health checks in place telling you that, as well as process managers/orchestrators making sure that the application started. You should not rely on developers digging through logs to see if the app is running or not, that’s a huge waste of time.
I agree that one should have health-, live-, ready-endpoints and similar, which an orchestrator can use to manage your application. Still, it doesn’t hurt to also log the application configuration. What’s a single more line of log? It will help you debug the problem faster.
Oh, I see what’s going on. Looks like logs is your only source of information about the past.
It may not be the only source of information about the past, but it’s usually one that most people have. And it’s often available to more people than the other sources are. So why not do it?
We are kinda implying that in 2022 you have things like infrastructure as code, you store configuration in git, you label your artifacts properly, etc.
All those things can be assumed. The database connection string example above was contrived. The point is that it’s possible to provide wrong information that’s semantically correct, so that the application doesn’t fall over on startup even though something is misconfigured.
My point is that you should probably think about how to prevent bad situations instead of band-aiding the problem solving process
Printing the application configuration is not band-aiding, but helping. Having this log statement will help lower the mean time to resolution, because it’s easy to get a hold of the configuration that the application was started with.
Sometimes you won’t see anything useful
Of course. But then the log statement didn’t hurt either.