If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeLet's start with a challenge: let's pretend that, to help us debug something, we want to log some messages from inside MarkdownHelper
. Okay: logging is work and work is done by services.... so let's go see if our project has a logger!
Find your terminal and run our favorite command:
php bin/console debug:autowiring log
There it is! We can use LoggerInterface
to get a service whose id is monolog.logger
. Notice that there are a bunch of loggers listed here. Ignore the others for now: we'll talk about them in a few minutes.
In MarkdownHelper
, how do we get access to a service that we need? It's the same process every time: add another constructor argument: LoggerInterface $logger
. To create a new property and set it, I'm going to use a PhpStorm shortcut. With my cursor on the argument, I'll hit Alt
+Enter
and select "Initialize properties":
... lines 1 - 5 | |
use Psr\Log\LoggerInterface; | |
... lines 7 - 8 | |
class MarkdownHelper | |
{ | |
... lines 11 - 13 | |
private $logger; | |
public function __construct(MarkdownParserInterface $markdownParser, CacheInterface $cache, bool $isDebug, LoggerInterface $logger) | |
{ | |
... lines 18 - 20 | |
$this->logger = $logger; | |
} | |
... lines 23 - 37 | |
} |
Nice! But it's not magic: that just created the property and set it down here.
In parse()
, let's add some very important code: if stripos($source,
'cat') !== false, then say $this->logger
and... let's use, ->info('Meow!')
:
... lines 1 - 8 | |
class MarkdownHelper | |
{ | |
... lines 11 - 23 | |
public function parse(string $source): string | |
{ | |
if (stripos($source, 'cat') !== false) { | |
$this->logger->info('Meow!'); | |
} | |
... lines 29 - 36 | |
} | |
} |
Let's take it for a spin! Move over, refresh... then click any link on the web debug toolbar to jump into the profiler. In the "Logs" section... there's our message!
The true reason we're doing this - other than to practice dependency injection - is to talk about how we can work with the many logger services in the container. Symfony's logging library - called Monolog - has this concept of logger channels. They're... kind of like logger categories and their useful because you can send logs from different channels to different files.
This is what you're seeing inside of debug:autowiring
: each logger "channel" is actually its own, unique logger service. The question is: we know how to get the "main" logger service, but how could we autowire one of these other loggers if we needed to?
Logging channels are not a super important concept - but it's a great example of this problem. To see how to handle it, let's add our own logger channel. The logger services comes from a bundle called MonologBundle
. By adding a little config to that bundle, we can get a shiny new logger channel.
In config/packages/
, create a new file called monolog.yaml
. Inside say monolog:
and below, set channels:
to an array. Let's create one new channel called markdown
:
monolog: | |
channels: ['markdown'] |
By the way, if you're surprised that there was no monolog.yaml
file by default, there actually is: there's one in the dev/
directory and another in prod/
. Loggers behave pretty differently in dev
versus prod
. Thanks to this new file, the markdown
channel will exist in all environments.
Anyways, now find your terminal and run debug:autowiring
:
php bin/console debug:autowiring log
Yes! The bundle created a new service for us called monolog.logger.markdown
.
So back to my original question: how can we get access to this logger? Well, this is already telling us! This says that if we type-hint an argument with LoggerInterface
and name the argument $markdownLogger
, it will pass us the monolog.logger.markdown
service.
Ok, let's try it! Back in MarkdownHelper
, rename the argument from $logger
to $markdownLogger
... and update the variable name below:
... lines 1 - 8 | |
class MarkdownHelper | |
{ | |
... lines 11 - 15 | |
public function __construct(MarkdownParserInterface $markdownParser, CacheInterface $cache, bool $isDebug, LoggerInterface $markdownLogger) | |
{ | |
... lines 18 - 20 | |
$this->logger = $markdownLogger; | |
} | |
... lines 23 - 37 | |
} |
Let's see what difference this makes. When we reload, it still works... but open up the profiler and go to the Logs section. Yes! There it is! It says "channel": markdown
. For this tutorial, I'm not really concerned about how or why we would use a different logger channel. The point is: this proves that we just fetched one of the other logger services.
The whole reason this works is because MonologBundle is smart: it sets up "autowiring aliases" for each channel. Basically, it makes sure that we can autowire the main logger with the type-hint or any of the other loggers with a type-hint and argument name combination. It sets all of that up for us, so we can just take advantage of it.
But what if it hadn't done that? Or, what if we needed to access one of the many lower-level services in the container that cannot be autowired? This is the last missing piece of the autowiring puzzle. Let's talk about it next.
Hey Joao P.
Have you tried to run clear:cache
command after adding markdown channel? It's totally cache issue also it can be related to opcache if you have it enabled in your php configuration.
Cheers!
I have tried clear:cache and it didn't work neither. It turns out I have placed the file in config directory not the child packages one.
also you can try restarting PHP workers, or the apache server depending on your configuration. In most cases, such an issue is cache related. Symfony caches all config files, and depending on OS and configuration from time to time you can face different issues with config files.
Cheers
Hint: in newer versions, isn't monology.yaml separated in dev and prod directiores. Now is only one file in packages directory. If add channels: ['markdown']
in when@dev
it will working well.
Definitely - new tricks in the latest version of Symfony! Thanks for making a note of that :)
FYI - if you add that channels
under when@dev
, just be careful because the "markdown logger" service will only exist in dev
: when you switch to prod
, that service won't exist... so you'll likely get an error. It's a bit safer to keep channels: ['markdown']
for all environments, but then don't use the "markdown" channel for anything special (e.g. don't log to its own file) in the prod
environment.
Cheers!
"php bin/console debug:autowiring log" shows only one service. I have added below the total scenario
<blockquote>Describes a logger instance.
Psr\Log\LoggerInterface (logger)</blockquote>
I dont find any monolog.logger in my app
Also profiler don't show any log message after adding this:-
`if (strpos($source, 'cat') !== false) {
$this->logger->info("meow");
}`
// composer.json
{
"require": {
"php": "^7.3.0 || ^8.0.0",
"ext-ctype": "*",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
"sensio/framework-extra-bundle": "^6.0", // v6.2.1
"sentry/sentry-symfony": "^4.0", // 4.0.3
"symfony/asset": "5.0.*", // v5.0.11
"symfony/console": "5.0.*", // v5.0.11
"symfony/debug-bundle": "5.0.*", // v5.0.11
"symfony/dotenv": "5.0.*", // v5.0.11
"symfony/flex": "^1.3.1", // v1.17.5
"symfony/framework-bundle": "5.0.*", // v5.0.11
"symfony/monolog-bundle": "^3.0", // v3.6.0
"symfony/profiler-pack": "*", // v1.0.5
"symfony/routing": "5.1.*", // v5.1.11
"symfony/twig-pack": "^1.0", // v1.0.1
"symfony/var-dumper": "5.0.*", // v5.0.11
"symfony/webpack-encore-bundle": "^1.7", // v1.8.0
"symfony/yaml": "5.0.*" // v5.0.11
},
"require-dev": {
"symfony/maker-bundle": "^1.15", // v1.23.0
"symfony/profiler-pack": "^1.0" // v1.0.5
}
}
Wierd. At first the markdown channel didn't show up on the console in dev, then I changed to prod and it appeared. Then I commented the monolog.yaml dev file and the console started to show the markdown channel also in the dev env, at last I've uncommented the monolog.yaml dev file but the markdown stayed. I suppose it was cache related.