1 - Make a single log, with a standardized format. Doesn't matter much what it is, but ensure that ever entry has the same basic fields. Just calling "printf" probably won't cut it ( substitute System.err.println or whatever as appropriate )
2 - Allow for at least one field to be an arbitrary string... the developer will know better then you what needs to be there.
3 - Include a high resolution time-stamp on each entry. You will need it eventually, trust me.
4 - If possible, include the file and line number of the origin of the error. That's easy in C, and a bit of a pain in Java. But it's incredibly useful later on, especially when people start to cut+paste code, including the error messages.
5 - Ensure the log is at a place that any level of the code can use it.
6 - I've often used "Primary" and "Secondary" error tags, where "Primary" means "I'm the guy who detected there is a problem", and "Secondary" means "I called a function which reported an error". That makes it easy to find the source of the problem ( "Primary: file not found" ) and still report the meaning of the error ( "Secondary: can't load calibration table" ).
7 - Include some capability to log non-errors as well as errors.
The hardest part I find is when an error isn't necessarily an error. If you call a function with a file, and the file doesn't exists, is that an error that should be logged or not? Sometimes it's a critical failure, and sometimes it's expected. It's pretty much up to the API of the function; if the function has a way to return an error, I will usually have it do that without logging; then it's the job of the higher level code to decide if it needs to report that error or if it is expected.