Assuming that your are using a relational database, you could simply code triggers on Insert, Update and Delete events to write an action or so within another table.
The idea of using a history table with the same definition as the original data table logged is also a good idea, according to me. This allows you to keep your original table as lightweight as possible, this increases the performance of DQL onto your table within the database, and you do not need to fetch every records when it comes to trace a Delete event onto a record that was accidently deleted by a user.
Another way, depending on the number of rows treated everyday, you may simply flag a field true or false whether it is deleted, and add a column that should save the current doing the action. Assuming that you configured the DB to work with Active Directory, so that you may identify with precision the current user.
While writing, I came accross another idea. You perhaps already thought about it yourself! :-) Anyway, you build yourself a DLL managing security matters. Within your code, you could simply pass a parameter with the currently logged user identified throguh your security DLL.
Whatsoever, log4net is I think a good way of doing it. You might perhaps log the events for every user into a SQLite DB which is a free DB for the personal edition. Microsoft Enterprise Library 4.1 - October 2008, if I remember correctly, offers such tools that are very useful and, as programmers, stuff that need to be redone and redone again and again become rapidly boring. MEL is designed especially to make these repeatable routines easier to avoid the pain of rewriting code over and over again. This allows you to concentrate upon what's important for your client, the features and requirements. It is OpenSource and maintained by Microsoft as per demand by such companies as IBM, HP and others big companies like these. Maybe some that we still ignore the existence. In anyway, I think Enterprise Library might be the best way to go, always considering your architecture, as it works almost by configuration only.
If you're used to reflection, you could also initialize an instance of your objects through reflection and while saving, having a routine which logs at the same time.
As you can see, there are many ways to make it possible to you without much efforts. It all depends on your architecture.
Well, I see I've written much. I hope not to bore anyone! :-)
Have a nice day!