The best practice is not to reinvent the wheel. It is using a log tool that has been well thought out and tested by professionals who are dedicated to this. And use these ready tools according to the specific recommendations.
Of course some cases are interesting to create a systems own, but you need to have a reason for this. I do not think ready solutions solve all the problems. But it does not seem to be your case, I think you might as well use something ready that solves those issues that you are concerned about.
These tools provide buffering solutions, load balancing and troubleshooting, write asynchronously and allow you to choose multiple targets to persist log.
Before passing some solutions I mean that log in database has to be well thought out, imagine that the database can be a great source of problems in certain circumstances (unless which is a SQLite, for example), then you would need to log in the problems that occurred in your log . Complicated, right?.
Some framework of log known for .net:
Log4Net - one of the most well-known and used based on Log4J.
NLog - One of the most modern and well-known. The only one I actually used.
Enterprise Library - Microsoft's official solution but it does not solve some things well.
Elmah - Highly recommended for use with ASP.Net. Very simple to use because it injects what is needed.
dotNetLog - Another well recommended and has some different features.
SeriLog - I do not know but it's one more to investigate and see if it suits you better.
There are others that are simpler but which are unsupported and some are abandoned. There are also some more complete but paid solutions, rarely worth it.
In general, these solutions only take care of log itself, requiring you to invoke your non-interception run that probably requires assembly rewrite tools using aspects. See complementary answer by Cigano Morrison Mendez.
There are still some tools to parse these logs , which helps justify the choice for a standard solution.