They have to use a different logger implementation for retail builds. It could just swallow/ignore log entries (and probably would ignore/disable a lot of logging channels), or pipe them to the game's console (maybe colouring channels), or a text file inside %appdata%, etc... You don't need to inherit an interface or anything to do this, a simple ifdef to select different implementations is often good enough.
I prefer a single logging object with multiple logging channels, where each channel can be of a different type. i.e. a FileLoggingChannel for writing to file, a ConsoleLoggingChannel for writing to the console, a SocketLoggingChannel for sending the logs over the network to an external logging application/server. Each channel type just implements a simple interface like:
class ILogChannel
{
public:
virtual void open(){}
virtual void close(){}
virtual void log( const LogMessage& msg ) = 0;
};
and is responsible only for outputting the log message object. The main logging object handles adding file, line, function info, process and thread IDs, timestamps, possibly even call stacks when logging exceptions/errors, etc, to the message object. All about flexibility. If you want to use an external debugging tool, the game/application can even detect the presence of the tool when it starts up and add a SocketLoggingChannel if the tool is running, or a FileLoggingChannel if it's not. Then you can pass the same build around to different machines without having to recompile or distribute the debugging tool (or just use a config file).
I would also like to point out that opening/closing files on every log entry as a way of flushing the buffers is a little silly. Unless you're using something like std::fstream that uses it's own internal stream buffer, you usually don't need to worry about a process crash; OS disk buffering is typically done via a system write-through cache that exists outside of the process space, so only a hard system crash/power failure would compromise the buffered data (and opening/closing a file does not force the data from the cache to the device anyway.) Even in cases where there is no system write-through cache, you can typically flush a buffer with much less overhead than open/close on every write.