A solution for logging to a text file

Hello.

–UPDATE: added contextual menuitem for inserting an try error block with logger statement within.
WHAT IT IS – README.txt
This is a solution which enables you to easily log the activity of a script to a separate
file to during debugging and experimenting to a predefined folder of your choice.
Copy the logger script object to the end of your script, create the global error handler AFTER the
put in logger statements where appropriate and off you go logging to a file which pops up
when the script is done, provided that you have installed a folder action on the logfile folder.

“EXAMPLE SKELETON THAT USES THE LOGGER SCRIPT WITHOUT THE ACTUAL SCRIPT”

tell logger to run -- MUST BE INCLUDED BEFORE YOUR MAIN ERROR HANDLER
try  -- MAIN ERROR HANDLER

        logger's logThis("Starting my script")
        --
        -- Your normal program is here.
        -- 
        logger's logThis("Last regular statement of the script has been executed")
        logger's flushLogFile()

on error e number n
	display dialog " " & e & " Number : " & n
	logger's flushLogFile()
end try

– the logger script object is place somewere here.

There is also a separate installer version which when installed can be reached from the context menu of the AppleScriptEditor which inserts the logger script object into your source file on a blank line,
together with the initial run statement. There are alose menu items for logger statments and flushLogFile. Even an alternative logger statement which writes to the file directly, -for those low
on memory. :wink:

Everything can easily be removed when the initial debugging of the script is finished. Or just let be if it was just an experiment.

WHAT IT CONSISTS OF
“README” - Description of the whole solution.

"INSTALLATION"                               - Installation instructions.

"VANILLA LOGGER SCRIPT"               - A script object for manually insertion into your script.

"APPLE SCRIPT LOGGER MENUITEM" - The same script but for installation into
                                                          AppleScript Editor's contextual menu.
                                 
"LOGGER STATEMENT"                     - Script to be installed into AppleScript Editor's contextual
                                                         menu. To insert a logger statement in your script.
                                 
"CLEANUP STATEMENT"                   - Script to be installed into AppleScript Editor's contextual
                                                         menu. To insert a flushLogFile statement in your on error block.
                                  
"ALTERNATIVE LOGGER STATEMENT"   - For those who don't whish to use the folder action.
                                                         Or runs low on memory. -They must call showLogfile explicitly.

"ALTERNATIVE CLEANUP STATEMENT" 

"CONTEXT MENU ITEM FOR INSERTING AND TRY ON ERROR BLOCK WITH LOG STATEMENT"
                                                          
 "REFERENCES"

HOW IT WORKS

Requirements
	A logger scriptobject must be inserted into your script and run.
	A Global error handler that calls the logger's flushLogFile handler.
	Insertion of a flushLogFile handler in every error handler.
	A call to the flushLogFile handler as the last statement in your script.
	
Preparations
	A script object must be is inserted into your script together with logging statements.
	The logger scriptobject must be run, and logger statement must be inserted at the places
	you want to keep track of stuff; stuff must be converted to text before passed to the 
	logger handler.

	You must insert logger statements in order to log various informations into the log file.
	You must insert a flushLogFile statement in order to close the file when you encounter a
	runtime error, this implies that you have to have a global runtime hander around your whole
	script or in the run handler of your script.
	
Runtime:
	The logger script object makes up the log file name of the name of script it resides in. 
	It then creates/overwrites any file with a similiar name in the log file folder. *NOTES
	It then outputs log statements you have inserted into the logfile when the script is finished.

NOTES

Cleanup: I tend to use a centralized handler which I stuff with everything that needs to be done before
I can exit both normally and abnormally.

Runtime:
If you want to compare the output of different versions of the script just name the scripts differently.

ex: 	
The different versions of the same script:
	myscript.scptd myscript01.scptd myscript02.scptd myscript03.scptd
will be store in your log directory as: 
	myscript.txt myscript01.txt myscript02.txt myscript03.txt

This makes it easy to compare the different results of the different versions if you either
use some commandline tool like diff from a terminal window, or if you choose to use filemerge
which can be found in the xcode developer tools. There are also some applescripts out there
which can be used for comparing text files.

The log files are stored as text files for easy access,and shows up autmagically in TextEdit
when the script is done.

REFERENCES

The basis where stolen from AppleScript Guidebook of Bill Cheesman.
Some subroutines reworked from AS the definitive Guide by Matt Neburg.
Some subroutines was looted from AS Sdk.

The Idea of making contextual menui tems in the AppleScript Editor was stolen from
AppleScript Developer Kit from scripts by Sal Sohoigan.

The basics where learnt by:
AppleScript the definitive Guide By Matt Neuburg.
The Applescript Language Guide
The Applescript Scripting Additions guide.
The Applescript Finder Guide
– EOF README

INSTALLATION

Create the folder to store the logger files in
( I have chosen to create one in my documents folder and create an alias to that folder onto
	my Desktop for easy access ).

The vanilla script for logging which you must manually insert into your script
	Copy "VANILLALOGGERSCRIPT" further below of this post and  paste it into your editor.
	Save it somewhere you can  easily find it.

	Edit the script and edit the path to the logfile in it.This is done by finding the text LOGPATH in
            the script and replace it whith the path to your logfiles folder.
	Simply remove "LOGPATH and just drag the Folder from finder into the script. Choose
	alias when prompted if you want to paste the entire contents.
	This script is later used as template and you  will copy its contents into the script you currently 
            want to log. 

The AppleScriptEditorMenuEditorItem
	Copy "APPLESCRIPTLOGGERMENUITEM" further below of this post and paste it into your editor.
	While using the AppleScript editor you can access the context menu by rightc clicking, select
           the menuitem for this script  and get the script inserted into your current script by pasting the                      
           clipboard.

	Edit the AppleScriptEditorMenuEditorItem 
	Find the text LOGPATH in the script, and store this script in "/Library/Scripts/Script Editor          
            Scripts/"  where ever you find it natural within this domain, so so that you can just right click in
           the current script file to copy the script into the clipboard to ease  installing the script.

The logger statement
	Copy the "LOGGER STATEMENT" from below into your editor and save it where ever you find it 
             natural within the "/Library/Scripts/Script Editor Scripts/"  domain. 
	Save it with the name LoggerStatement.
	This is so that you can just right click in the current script to insert the logger statement onto 
            the clipboard which you then can paste into your script.

The flushLogFile statement
	Copy the "FLUSH STATEMENT" from below into your editor and save it where ever you find it 
            natural within the "/Library/Scripts/Script Editor Scripts/"  domain. 
	Save it with the name CleanupStatement.
	This is so that you can just right click in the current script to insert the flushLogFile statement onto 
           the clipboard which you then can paste into your script.


If you have followed the steps you found neccesary you are now good to go!

– EOF INSTALLATION

“VANILLALOGGERSCRIPT”

– see README in the post for explanation. If no copyright is mentioned it is mine
– but put in the public domain. Use it as you will but if you republish anything with
– explicit copyright notice over it; please keep the copyright notice .

tell logger to run -- MUST BE INCLUDED BEFORE YOUR MAIN ERROR HANDLER

script logger
	property logFolderPath : "LOGPATH" -- posixpath
	
	on run
		global logList, logFileName
		set myName to my internal's myShortName()
		set pxLogfileName to logFolderPath & "/" & myName & ".txt"
		set mydata to "This is all my data so far\n\t\t and so be it"
		
		if my internal's posixPathExist(pxLogfileName) then
			if not my internal's deletePosixFile(pxLogfileName) then
				display alert "Logger: Couldn't delete " & pxLogfileName & " hmm. exiting"
				error number -128
			end if
		end if
		set logFileName to my internal's macStylePathName(pxLogfileName)
		set logFileName to the logFileName as text
		-- prep of the list to store data in 
		set logList to {}
	        -- :)
	end run
	
	on logThis(theStatement) -- ex: logger's logThis(" a has now a value of : " & a )
		set theStatement to theStatement & "\n"
		set end of my logList to theStatement
	end logThis
	
	on logThisDirectly(theStatement) -- for those low on memory
		set theStatement to theStatement & "\n"
		try
			set the open_target_file to open for access the file (my logFileName) with write permission
			write this_data to the open_target_file starting at eof
			close access the open_target_file
			return true
		on error
			try
				close access the open_target_file
				display alert "Logger: IOerror in " & pxLogfileName & "While trying to close the file exiting"
				error number -128
			end try
			return false
		end try
		
	end logThisDirectly
	
	on flushLogFile() -- flushes the log into logfile on disk. ( the flushLogFile )
		try
			
			set the open_target_file to open for access the file (my logFileName) with write permission
			set eof of the open_target_file to 0 --> remove this line if you want written data appended to existing data
			repeat with aLogStatement in my logList
				write aLogStatement to the open_target_file starting at eof
			end repeat
			close access the open_target_file
		on error
			try
				close access the open_target_file
				display alert "Logger: IOerror in " & pxLogfileName & "While trying to close the file exiting"
			end try
			return false
		end try
		my showLogFile()
		
	end flushLogFile
	
	on showLogFile() -- intended to be used with logThisDirectly
		tell application "Finder" to open my logFileName as alias
	end showLogFile
	script internal
		
		on macStylePathName(posixStylePathName)
			return (POSIX file posixStylePathName as Unicode text)
		end macStylePathName
		
		on deletePosixFile(posixFn)
			local fRef, success
			if not posixPathExist(posixFn) then return missing value
			
			set success to true
			set fRef to a reference to POSIX file posixFn as alias
			tell application "Finder"
				try
					delete file fRef as alias
				on error
					set success to false
				end try
			end tell
			return success
		end deletePosixFile
		
		on posixPathExist(s)
			try
				POSIX file s as alias
			on error
				return false
			end try
			return true
		end posixPathExist
		
		on myShortName()
			local myNameShort
			set myNameShort to my extract_shortname_from(extract_filename_from(path to me as Unicode text))
			log myNameShort
			return myNameShort
		end myShortName
		(* Sal Soghoian ©1998 Apple Computer *)
		on extract_filename_from(the_filepath)
			set the_filepath to the_filepath as text
			set x to the offset of ":" in (the reverse of every character of the_filepath) as string
			return ((characters -(x - 1) thru -1 of the_filepath) as text)
		end extract_filename_from
		(* Sal Soghoian ©1998 Apple Computer *)
		on extract_shortname_from(the_filepath)
			set the_filepath to the_filepath as text
			set x to the offset of "." in (the reverse of every character of the_filepath) as string
			--	return ((characters -(x - 1) thru -1 of the_filepath) as text)
			return ((characters 1 thru -(x + 1) of the_filepath) as text)
		end extract_shortname_from
	end script
end script

EOF “VANILLALOGGERSCRIPT”

“APPLE SCRIPT LOGGER MENUITEM”

set the clipboard to "tell logger to run" -- MUST BE INCLUDED BEFORE YOUR MAIN ERROR HANDLER
script logger
	property logFolderPath : \"LOGPATH\" -- posixpath
	
	on run
		global logList, logFileName
		set myName to my internal's myShortName()
		set pxLogfileName to logFolderPath & \"/\" & myName & \".txt\"
		set mydata to \"This is all my data so far\n\t\t and so be it\"
		
		if my internal's posixPathExist(pxLogfileName) then
			if not my internal's deletePosixFile(pxLogfileName) then
				display alert \"Logger: Couldn't delete \" & pxLogfileName & \" hmm. exiting\"
				error number -128
			end if
		end if
		set logFileName to my internal's macStylePathName(pxLogfileName)
		set logFileName to the logFileName as text
		-- prep of the list to store data in 
		set logList to {}
		
	end run
	
	on logThis(theStatement) -- ex: logger's logThis(\" a has now a value of : \" & a )
		set theStatement to theStatement & \"\n\"
		set end of my logList to theStatement
	end logThis
	
	on logThisDirectly(theStatement) -- for those low on memory
		set theStatement to theStatement & \"\n\"
		try
			set the open_target_file to open for access the file (my logFileName) with write permission
			write this_data to the open_target_file starting at eof
			close access the open_target_file
			return true
		on error
			try
				close access the open_target_file
				display alert \"Logger: IOerror in \" & pxLogfileName & \"While trying to close the file exiting\"
				error number -128
			end try
			return false
		end try
		
	end logThisDirectly
	
	on flushLogFile() -- flushes the log into logfile on disk. ( the flushLogFile )
		try
			
			set the open_target_file to open for access the file (my logFileName) with write permission
			set eof of the open_target_file to 0 --> remove this line if you want written data appended to existing data
			repeat with aLogStatement in my logList
				write aLogStatement to the open_target_file starting at eof
			end repeat
			close access the open_target_file
		on error
			try
				close access the open_target_file
				display alert \"Logger: IOerror in \" & pxLogfileName & \"While trying to close the file exiting\"
			end try
			return false
		end try
		my showLogFile()
		
	end flushLogFile
	
	on showLogFile() -- intended to be used with logThisDirectly
		tell application \"Finder\" to open my logFileName as alias
	end showLogFile
	script internal
		
		on macStylePathName(posixStylePathName)
			return (POSIX file posixStylePathName as Unicode text)
		end macStylePathName
		
		on deletePosixFile(posixFn)
			local fRef, success
			if not posixPathExist(posixFn) then return missing value
			
			set success to true
			set fRef to a reference to POSIX file posixFn as alias
			tell application \"Finder\"
				try
					delete file fRef as alias
				on error
					set success to false
				end try
			end tell
			return success
		end deletePosixFile
		
		on posixPathExist(s)
			try
				POSIX file s as alias
			on error
				return false
			end try
			return true
		end posixPathExist
		
		on myShortName()
			local myNameShort
			set myNameShort to my extract_shortname_from(extract_filename_from(path to me as Unicode text))
			log myNameShort
			return myNameShort
		end myShortName
		(* Sal Soghoian ©1998 Apple Computer *)
		on extract_filename_from(the_filepath)
			set the_filepath to the_filepath as text
			set x to the offset of \":\" in (the reverse of every character of the_filepath) as string
			return ((characters -(x - 1) thru -1 of the_filepath) as text)
		end extract_filename_from
		(* Sal Soghoian ©1998 Apple Computer *)
		on extract_shortname_from(the_filepath)
			set the_filepath to the_filepath as text
			set x to the offset of \".\" in (the reverse of every character of the_filepath) as string
			--	return ((characters -(x - 1) thru -1 of the_filepath) as text)
			return ((characters 1 thru -(x + 1) of the_filepath) as text)
		end extract_shortname_from
	end script
end script"

EOF “APPLE SCRIPT LOGGER MENUITEM”

“LOGGER STATEMENT”

set the clipboard to "logger's logThis(\"\")"

EOF “LOGGER STATEMENT”

“CLEANUP STATEMENT”

set the clipboard to "logger's flushLogFile()"

EOF “CLEANUP STATEMENT”

“ALTERNATIVE LOGGER STATEMENT”

set the clipbard to "logger's logThisDirectly(\"\")"

EOF “ALTERNATIVE LOGGER STATEMENT”

“ALTERNATIVE CLEANUP STATEMENT”

set the clipbard to "logger's showLogFile()"

EOF “ALTERNATIVE CLEANUP STATEMENT”

“CONTEXT MENU ITEM FOR INSERTING AND TRY ON ERROR BLOCK WITH LOG STATEMENT”


(*
Message and OK

Copyright © 2001“2007 Apple Inc.

You may incorporate this Apple sample code into your program(s) without
restriction.  This Apple sample code has been provided "AS IS" and the
responsibility for its operation is yours.  You are not permitted to
redistribute this Apple sample code as "Apple sample code" after having
made changes.  If you're going to redistribute the code, we require
that you make it clear that the code was descended from Apple sample
code, but that you've made changes.
------------------------------------------------------------------------------
This script was originally created by apple but are modified to me in order to 
insert an error handler with a logger statement. into a try errror block.
Save it in the Machines Script Editor Scripts 's Error handlers and name it

To use it, select som code you'd like to wrap an error handler block about
and run the script by right clicking. - or using it from the scripts menu.
-------------------------------------------------------------------------------
*)

set CR to ASCII character 13
set NL to ASCII character 10

tell application "AppleScript Editor"
	tell front document
		set the target_string to "--XXXX"
		
		set the selected_text to contents of selection
		
		if the selected_text is "" then
			set the selected_text to the target_string
			set the script_text to ""
			set the script_text to the script_text & "try" & return
			set the script_text to the script_text & tab & the target_string & return
			set the script_text to the script_text & "on error the error_message number the error_number" & return
			set the script_text to the script_text & tab & "-- display dialog \"Error: \" & the error_number & \". \" & the error_message buttons {\"OK\"} default button 1" & return
			set the script_text to the script_text & tab & "logger's logThis(\"Error: \" & the error_number & \". \" & the error_message)" & return
			set the script_text to the script_text & "end try" & return
			set the replacement_string to "-- insert actions here"
		else
			set the script_text to ""
			set the script_text to the script_text & "try" & return
			if last character of selected_text is in {CR, NL} then
				set the script_text to the script_text & tab & the selected_text
			else
				set the script_text to the script_text & tab & the selected_text & CR
			end if
			set the script_text to the script_text & tab & "--display dialog \"Error: \" & the error_number & \". \" & the error_message buttons {\"OK\"} default button 1" & return
			set the script_text to the script_text & "on error the error_message number the error_number" & return
			set the script_text to the script_text & tab & "logger's logThis(\"Error: \" & the error_number & \". \" & the error_message)" & return
			set the script_text to the script_text & "end try" & the target_string & return
			set the replacement_string to ""
		end if
		
		set contents of selection to script_text
		
		try
			check syntax
		end try
		
		my replace_and_select(target_string, replacement_string)
		
		try
			check syntax
		end try
	end tell
end tell

on replace_and_select(target_string, replacement_string)
	tell application "AppleScript Editor"
		tell the front document
			set this_text to the contents
			set this_offset to the offset of the target_string in this_text
			if this_offset is not 0 then
				set selection to characters this_offset thru (this_offset + (length of the target_string) - 1)
				set the contents of the selection to the replacement_string
			end if
		end tell
	end tell
end replace_and_select

EOF “CONTEXT MENU ITEM FOR INSERTING AND TRY ON ERROR BLOCK WITH LOG STATEMENT”