The reason for this weird practice in #define's is to encapsulate the different assignments within a loop that is executed exactly once, so one may use the macro like a function. For example, with the code you posted, one can write:
if(...)
LOG(x, y);
else
// Something else
and it is expanded as
if(...)
do {...} while(false);
else
// Something else
This would not work without the do...while(false) surrounding the different assignments, because that would be expanded as
if(...)
Lock lock (logMutex);
// Other code... Outside the if statement!
Also forcing a semicolon after the macro makes it look like a function and you wont get errors because you added an semicolon like after a normal function.