I am working a project in delphi and I am creating a installer for the application,
there are Three main parts.
- PostgreSQL installation/uninstallation
- myapplication ( setup of myapplication is created using nsi) installation/uninstallation.
- Creating tables in Postgres through script(batch files).
Every thing runs fine and smoothly, but if something fails I have created a LogToFileger which will LogToFile every step of the process,
like this
LogToFileToFile.LogToFile('[DatabaseInstallation] : [ACTION]:Postgres installation started');
The function LogToFileToFile.LogToFile()
This will write the contents to a file. This is working nicely, but the problem is this has messed up the code as in it has become difficult to read the code as one ca only see the LogToFileToFile.LogToFile()
function call everywhere in the code
an example
if Not FileExists(SystemDrive+'\FileName.txt') then
begin
if CopyFile(PChar(FilePathBase+'FileName.txt'), PChar(SystemDrive+'\FileName.txt'), False) then
LogToFileToFile.LogToFile('[DatabaseInstallation] : copying FileName.txt to '+SystemDrive+'\ done')
else
LogToFileToFile.LogToFile('[DatabaseInstallation] : copying FileName.txt to '+SystemDrive+'\ Failed');
end;
if Not FileExists(SystemDrive+'\SecondFileName.txt') then
begin
if CopyFile(PChar(FilePathBase+'SecondFileName.txt'), PChar('c:\SecondFileName.txt'), False) then
LogToFileToFile.LogToFile('[DatabaseInstallation] : copying SecondFileName.txt to '+SystemDrive+'\ done')
else
LogToFileToFile.LogToFile('[DatabaseInstallation] : copying SecondFileName.txt to '+SystemDrive+'\ Failed');
end;
as you can see there are lots of LogToFileToFile.LogToFile()
calls,
before it was
if Not FileExists(SystemDrive+'\FileName.txt') then
CopyFile(PChar(FilePathBase+'FileName.txt'), PChar(SystemDrive+'\FileName.txt'), False)
if Not FileExists(SystemDrive+'\SecondFileName.txt') then
CopyFile(PChar(FilePathBase+'SecondFileName.txt'), PChar('c:\SecondFileName.txt'), False)
this is the case in my whole code now.
its difficult to read.
can any one suggest me a nice way to unclutter the calls to LogToFile?
like
-
Indenting the ' LogToFileToFile.LogToFile()` call
like thisif Not FileExists(SystemDrive+'\FileName.txt') then begin if CopyFile(PChar(FilePathBase+'FileName.txt'), PChar(SystemDrive+'\FileName.txt'), False) then {Far away--->>} LogToFileToFile.LogToFile(2,'[DatabaseInstallation] : [ACTION]:copying FileName.txt to '+SystemDrive+'\ sucessful') else {Far away--->>} LogToFileToFile.LogToFile(2,'[DatabaseInstallation] : [ACTION]:copying FileName.txt to '+SystemDrive+'\ Failed'); end;
-
Separate unit like
LogToFileger
This unit will have all the LogToFile messages in aswitch case
like thisFunction LogToFilegingMyMessage(LogToFilegMessage : integer) begin case LogToFilegMessage of 1 : LogToFileToFile.LogToFile(2,'[DatabaseInstallation] : [ACTION]:copying FileName.txt to '+SystemDrive+'\ sucessful'); 2 : LogToFileToFile.LogToFile(2,'[DatabaseInstallation] : [ACTION]:copying FileName.txt to '+SystemDrive+'\ Failed'); 150 : LogToFileToFile.LogToFile(2,'[somthing] : [ACTION]: somthing important); end;
so I can just call the LogToFilegingMyMessage(1) where ever required.
Can anyone tell me which is a better and cleaner approach to LogToFileging this way?
Best Answer
When you added logging, you introduced two things:
Each of these has problems has its own, relatively simple solution:
Break the code up into smaller functions. Instead of having one giant function that contains all your copies as well as log messages for error/success, you could introduce a function "CopyFile", that would copy exactly one file and logs it's own result. That way your main code would just consist of CopyFile calls and would remain easy to read.
You could make your logger smarter. Instead of passing in a giant string that has a lot of repetitive information, you could pass in enumerations values that would make things clearer. Or you could define more specialized Log() functions, i.e. LogFileCopy, LogDbInsert... Whatever you repeat a lot, consider factoring that out into its own function.
If you follow (1), you could have code that looks like this:
Then your CopyFile() just needs few lines of code to do the action and log its result, so all your code remains concise and easy to read.
I would stay away from your approach #2 as you are detaching information that should stay together into different modules. You are just asking for your main code to get out of sync with your log statements. But looking at LogMyMessage( 5 ), you'll never know that.
UPDATE (response to comment): I'm not familiar with exact language you are using, so this part might have to be adapted a bit. It seems all your log messages identify 3 things: component, action, outcome.
I think this is pretty much what MainMa suggested. Instead of passing actual string, define constants (in C/C++/C#, they would be part of enum enumeration type). So for example for components, you might have: DbInstall, AppFiles, Registry, Shortcuts... Anything that make the code smaller will make it easy to read.
It would also help if your language supported variable parameter passing, not sure if that's possible. So for example if action is "FileCopy", you could define that action to have two additional user parameters: file name and destination directory.
So your file copying lines would look something like this:
*note, there's also no reason to copy/paste the log line twice if you can store the result of the operation in a separate local variable and just pass that variable into Log().
You see the theme here, right? Less repetitive code -> more readable code.