Your Application is Silent — That's a Bad Sign
\n\nHave you ever found out about a bug in production from an angry user? If yes — you know how painful it is. If no — you're lucky (for now).
\n\nPHP is a language that powers 78% of all websites in the world. But it has one peculiarity: it's too "quiet". By default, PHP will simply swallow an error, show a white screen, or, even worse, continue working with incorrect data. Without a monitoring and logging system, you are blind.
\n\nIn this article, we'll break down how to turn your PHP application from a "black box" into a transparent system where every error, every slow request, and every suspicious action is recorded. Let's go!
\n\nWhy "echo $error" is Not Logging?
\n\nMany beginner developers write:
\n\ntry {\n // some code\n} catch (Exception $e) {\n echo $e->getMessage();\n}\n\n\nThis is not logging. This is shouting into the void. Outputting to the browser is not saved, not structured, and gives you no analytics. Real logging is when you can:
\n\n- \n
- See an error 5 minutes after it occurs \n
- Understand which server and environment it happened on \n
- Filter through thousands of records and find the exact one \n
- Get a notification in Telegram/Slack if something breaks \n
Let's move on to the tools that can do this.
\n\nMonolog — The De Facto Standard for PHP
\n\nIf you write in PHP and don't use Monolog, you are most likely reinventing the wheel. This library has become the standard for logging in PHP (yes, Laravel and Symfony use it under the hood).
\n\nInstallation and a Simple Example
\n\ncomposer require monolog/monolog\n\n\n<?php\nuse Monolog\\Logger;\nuse Monolog\\Handler\\StreamHandler;\n\n// Create a log channel\n$log = new Logger('my_app');\n// Write to file /var/log/my_app.log\n$log->pushHandler(new StreamHandler('/var/log/my_app.log', Logger::WARNING));\n\n// Use it\n$log->warning('This is a warning');\n$log->error('Critical error: database unavailable');\n\n\nThat's better. But Monolog can do much more. You can send logs to different places simultaneously:
\n\n- \n
- To a file — for local debugging \n
- To Elasticsearch — for full-fledged search \n
- To Telegram/Slack — for urgent notifications \n
- To Syslog — for system administration \n
Advanced Example with Multiple Handlers
\n\n<?php\nuse Monolog\\Logger;\nuse Monolog\\Handler\\StreamHandler;\nuse Monolog\\Handler\\TelegramBotHandler;\nuse Monolog\\Handler\\ElasticsearchHandler;\nuse Monolog\\Formatter\\LineFormatter;\n\n$log = new Logger('production');\n\n// Write all errors to a file\n$fileHandler = new StreamHandler('/var/log/app.log', Logger::DEBUG);\n$log->pushHandler($fileHandler);\n\n// Critical errors — straight to Telegram\n$telegramHandler = new TelegramBotHandler(\n 'YOUR_BOT_TOKEN', \n 'YOUR_CHAT_ID', \n Logger::CRITICAL\n);\n$telegramHandler->setFormatter(new LineFormatter(\n "[%datetime%] %channel%.%level_name%: %message% %context%\\n"\n));\n$log->pushHandler($telegramHandler);\n\n// Log\n$log->critical('Database is down', ['server' => gethostname()]);\n\n\nNow, if the database goes down at night, you'll find out via a notification on your phone. Not in the morning from a client.
\n\nSentry — Error Monitoring with Context
\n\nMonolog is great for writing logs, but when there are many errors, just reading files is a nightmare. This is where Sentry comes to the rescue — an error collection system with a web interface.
\n\nSentry automatically collects:
\n- \n
- Stack trace \n
- Request data (URL, headers, body) \n
- User information (if provided) \n
- Variable state