Conditional Agent LoggingAgent logging is a great feature that's been around in Notes ever since LotusScript has been around (starting in Notes R4). But it's something not everyone takes advantage of. One of the reasons (IMHO) for this is because way too much information is logged every time the agent runs, causing an overload of information to be written to your agent log, that you don't look at anyway. Logging is one of those things that you wish you had in there, but only when you need it. This tip talks about implementing what I call "conditional logging" for your scheduled agents.
What I mean by "conditional logging" is logging that's only enabled when you want it to be enabled. Most of the time, logging won't be enabled. Then, when you need it (because your agent isn't processing what it's supposed to be processing or there's some other problem) you can enable the logging without having to update any of your code.
What about agent performance? There is going to be a very slight hit in performance, but I highly doubt it will be recognized. Writing documents to an agent log is a very efficient process, and when logging is disabled, the code doesn't do much extra that will affect your agent's overall performance. If you're having performance problems with your agent, logging is one good way to track down the problem, anyway. (Another alternative is Agent Profiling, new to Notes 7, but I'm talking about logging here).
To enable "conditional logging", you first need a profile document that controls the log settings. The profile has an alias of "profLogging" and can be called whatever you want. You'll need to have some way of editing the profile, through @Command([EditProfile] "profLogging"). There are only two fields on the profile document. The first is called Enabled and is a radio button field with values of Yes and No. This indicates whether logging is enabled in this database or not. The second is called Path and lists the path to the agent log database. I use one log database for all my scheduled agents. That way, I don't have to deploy a new agent log every time I deploy a new database. I'll show later on how to distinguish the agents within the agent log.
In the LotusScript agent, it checks to see if agent logging is enabled in the profile. If it is, it sets everything up. If it isn't, there an indicator that we'll use later on to state that logging isn't being used.
Dim session As New NotesSession
Dim currDb As NotesDatabase
Dim currAgent As NotesAgent
Dim profile As NotesDocument
Dim agentLog As NotesLog
Set agentLog = Nothing ' Default - logging not being used
Set currDb = session.CurrentDatabase
Set currAgent = session.CurrentAgent
Set profile = currDb.GetProfileDocument("profLogging")
If profile.HasItem("Enabled") And profile.HasItem("Path") Then ' If profile set up
If profile.GetItemValue("Enabled")(0) = "Yes" Then ' If logging enabled
Set agentLog = New NotesLog(currDb.Title & "\" & currAgent.Name)
If session.IsOnServer Then ' If running on schedule
Call agentLog.OpenNotesLog("", profile.GetItemValue("Path")(0))
Else ' Manually kicked off by client
Call agentLog.OpenNotesLog(currDb.Server, profile.GetItemValue("Path")(0))
The agent makes sure the profile document is properly set up. Note that when you make the call to GetProfileDocument, if the profile doesn't exist, one will be created at that time. So that line should always return a valid handle to a document. So I don't check for the profile being equal to Nothing. Instead, I check to see if it has the fields I'm going to use. If the profile document doesn't exist, then it won't have those fields after it's created by this agent. In that case, agent logging will be disabled.
If the profile says that agent logging is enabled, then I create a new NotesLog object. I give it a name of the database title, then a slash, then the agent name. This is how I make use of one logging database for all my agents. When I look at the view in that logging database, all my agents will be categorized first by the database title, then further categorized by the agent name. So I can find the log documents easily by going to the right category and subcategory. The agent then initializes itself differently depending on whether the agent is running on a schedule (session.IsOnServer is True) or not. If running on a schedule, then the logging is done locally. If the agent is kicked off manually, then the logging can't be done locally - it needs to go to the server.
(Yes, for those of you that want to point it out, currDb.Server returns an empty string for agents running on a schedule. So, technically, I don't need to check whether the session is on the server or not. I could just use currDb.Server and achieve the same effect. I personally prefer this method because I can look at the code later on and know instantly that I allowed the agent to be run locally in addition to running on schedule. Some scheduled agents will fail if you try to run them locally because of different things. This code says, from just a glance, that I took both environments into account.)
When I am ready to log something to my log, I need to pass around the agentLog object to subroutines and functions. That object is either initialized or not, depending on whether logging is enabled or not. So I check to see if the object has been initialized and, if so, do my logging:
If Not agentLog Is Nothing Then Call agentLog.LogAction("This is the information to write to the log")
The log record will automatically be time stamped, so you don't have to worry about that. When I want to track down a problem with an agent that has logging set up, I can simply enable logging through the profile document and let the agent run on whatever schedule it currently has. Then I can look at the logging database to track down the problem. When finished, I go back into the profile document and disable logging. It's that simple. There shouldn't be any noticeable performance problems when logging is disabled over not having any logging at all - it's a very quick operation to check to see if an object is defined or not. And having this code in the agent from the start really helps when you need it down the road - you don't have to go in and put in a bunch of statements into your code and re-deploy, then take them out and re-deploy again when finished.