Truncating a logfile actually works because the writers open the files for writing using O_APPEND.
From the open(2) man page:
O_APPEND: The file is opened in append mode. Before each write(2), the
file offset is positioned at the end of the file, as if with lseek(2).
The modification of the file offset and the write operation are
performed as a single atomic step.
As mentioned, the operation is atomic, so whenever a write is issued, it will append to the current offset matching the end of file, not the one saved before the previous write operation completed.
This makes an append work after a truncate operation, writing the next log line to the beginning of the file again, without the need to reopen the file.
(The same feature of O_APPEND also makes it possible to have multiple writers appending to the same file, without clobbering each other's updates.)
The loggers also write a log line using a single write(2) operation, to prevent a log line from being broken in two during a truncate or concurrent write operation.
Note that loggers like syslog, syslog-ng or rsyslog typically don't need to use
copytruncate since they have support to reopen the log files, usually by sending them a SIGHUP. logrotate's support for
copytruncate exists to cater for other loggers which typically append to logfiles but that don't necessarily have a good way to reopen the logfile (so rotation by renaming doesn't work in those cases.)
Please note also that
copyrotate has an inherent race condition, in that it's possible that the writer will append a line to the logfile just after logrotate finished the copy and before it has issued the truncate operation. That race condition would cause it to lose those lines of log forever. That's why rotating logs using
copytruncate is usually not recommended, unless it's the only possible way to do it.