It should be inserted literally right next to it's first use case. Your IDE will literally point it to you with red squigglys because the places where you've added a dependency will be missing a parameter. Go to the highest one and add it on the line above.
I've never seen a tree graph, not without lots of global mutable state to cheat around DI. Your logger is just going to be needed almost everywhere.
What do you do on shutdown? In languages with destructors, that can automatically give you a call order in reverse of the construction order, but in Go you end up manually ordering things or just not having panicless shutdowns.
Okay, it's not a tree. Because multiple objects will depend on something like a logger. But it's an acyclic graph if designed properly. Which is incredibly simple to setup and teardown.
If your loggers are needed everywhere, then you just pass them as a constructor to the objects that need them. You're literally doing this with fx anyway.
Like, a logger is probably the first thing you new up in main(). So now you can pass it down as a dependency in constructors.
For shutdown you just defer your shutdown functions. Have a basic interface where your services have a Shutdown() method and then you can push them onto a stack and pop them off during shutdown.
There's no manual ordering involved. Your initialisation is a linear top down process, your shutdown is bottom up. It can't be any simpler. If you keep code as close to usage sites then there's only 1 possible order.
I agree with you on all of this. fx is not doing much more for shutdown than what you describe (calling a handler pushed to a stack created during initialization). Instead of implementing this for every app, I just prefer to use a library with great documentation and tests.
It should be inserted literally right next to it's first use case. Your IDE will literally point it to you with red squigglys because the places where you've added a dependency will be missing a parameter. Go to the highest one and add it on the line above.