Assuming you want to keep this in the app layer I would advice to use a model layer with properties per field to do the logging. All data access then goes trough this data model, giving you a hook to add functionality. 
An activerecord based example (VBScript):
   class cSomeEntity
       public db ' link to a db wrapper
       private id, dirty, loaded ' varous flags 
       private sub class_initialize
         dirty = false
         loaded = false
       end sub
       private sub class_terminate
        if dirty then
            db.execute("update some_table set some_field=? where id=?", array(p_some_field, id))
        end if
       end sub
       public sub load_by_id(value)
         dim rs
         set rs = db.fetch_rs("select id, some_field from some_table where id=?", array(id))
         id = rs("id")
         p_some_field = rs("some_field")        
         loaded = true
       end sub
       private p_some_field
       public property get some_field
         some_field = p_some_field
       end property
       public property let some_field(value)
         if not loaded then err.raise 1, , "Entity not yet initialized, call .load_by_id() first!"
         if value <> p_some_field then
           dirty = true
           make_log_entry("some_value", p_some_field, value)           
           p_some_field = value      
         end if
       end property
       private sub make_log_entry(field, old_value, new_value)
         db.execute("insert into audit_log (table, field, old_value, new_value) values (?, ?, ?, ?)", _
           array("some_table", field, old_value, new_value))     
       end sub 
    end class
It might seem a bit bloated, but it is more flexible than a trigger based approach. For example you can easily implement range checks and such. 
Secondly, when you need to write multiple entity classes you can push a lot of functionality in to a delegate class and use code template to write the property getter & setters.