It sounds like this application is just supposed to send some data somewhere and exit, right?
If so, then a couple of options:
1. If the application is supposed to exit, the FileSenderHandler knows when it has finished writing the data, right? When it's done, call System.exit(0). Do your logging in the finally block in your main method.
2. Pass FileSenderHandler a reference to the EventLoopGroup, and have it call shutdown() on it when the data has been sent, acknowledged, yadda yadda. Or call channel.close(), which will also bail you out of sync().
Sounds like you're missing a few details on how Netty works. First, the main thread does nothing except start a thread pool and open a connection, and then wait for something else to close that connection. All the real work happens, not on the main thread at all, but on background threads created by the EventLoopGroup. So while the *main thread* is blocked, you can always toss your own runnables or callables into the EventLoopGroup and get them scheduled to run in the background (so you can do whatever you want, or wait for whatever event you like, while the main thread is blocked). From what you wrote, I got the impression you were thinking all the work was happening synchronously in the main thread, and there was no way to do anything while it's blocked.
You block the main thread in Netty because all the other threads in the application are *daemon* threads - ones that, to the JVM, don't count as the application "doing anything" that means it isn't done with its work. So if you didn't block on sync() in your one non-daemon, the JVM would decide it had nothing left to do and the program would exit before your socket was even open. That's why the pattern of blocking the main thread is there. And if you block your only non-daemon thread, but your program does have a clear-cut exit point, then the code that knows when you hit that point needs to do something to unblock it (this is why Thread.interrupt() exists, but closing the thing it's blocked on is what you should do when you can).
Anyway, you're probably better off doing your logging in the thing that writes the data, rather than waiting until everything's done (so if something fails, you have some idea of where it got to without a debugger).
HTH,
-Tim