Using the IAPILog Interface for logging
- Notes:
- To access logs for a custom application, you can use the log extractor available through the RelativityOne UI. For more information, see Log Extractor on the RelativityOne documentation site.
- You can't log from a standalone console application.
- Consider using the External Logging approach for finer logging control, and to integrate your Relativity applications with external and third-party logging solutions.
Before you begin
Before using logging in a custom application, make sure logging is enabled in Relativity to write to an appropriate sink. If you want to use the defaults, make sure the default configuration row in the RelativityLogging.Configuration table is set to True (1). This enables logging to the EDDSLogging.RelativityLogs table at the Error (4) level. .
IAPILog interface
The IAPILog interface included in the Relativity API namespace enables logging functionality. These IAPILog interface methods correspond to the available logging levels:
public interface IAPILog {
void LogVerbose(string messageTemplate, params object[] propertyValues);
void LogDebug(string messageTemplate, params object[] propertyValues);
void LogInformation(string messageTemplate, params object[] propertyValues);
void LogWarning(string messageTemplate, params object[] propertyValues);
void LogError(string messageTemplate, params object[] propertyValues);
void LogFatal(string messageTemplate, params object[] propertyValues);
...
}
When called from the code, the methods logs the events at the specified level and higher when the events occur. For example, the following code logs Debug, Information, Warning, Error, and Fatal-level events. The events may occur when cleaning Kepler Service Host temporary directories.
Logger.LogDebug("Cleaning up any left over files and folders in the Kepler Service Host temporary directories");
Each method can also take an exception object. This outputs the exception and stack trace into a separate field in the database to assist with searching.
public interface IAPILog {
...
void LogVerbose(Exception exception, string messageTemplate, params object[] propertyValues);
void LogDebug(Exception exception, string messageTemplate, params object[] propertyValues);
void LogInformation(Exception exception, string messageTemplate, params object[] propertyValues);
void LogWarning(Exception exception, string messageTemplate, params object[] propertyValues);
void LogError(Exception exception, string messageTemplate, params object[] propertyValues);
void LogFatal(Exception exception, string messageTemplate, params object[] propertyValues);
...
}
Add loggers using API helpers
Logging is built into the Relativity infrastructure and can be accessed using the API helpers. You can call the loggers from applications components, such as agents, custom pages, and event handlers.
The following code sample demonstrates how to call a default logger from an agent:
- Instantiate the logger. Copy
private Relativity.API.IAPILog _logger;
_logger = this.Helper.GetLoggerFactory().GetLogger().ForContext<MyAgent>();Note: It is recommended to always scope a logger to a class using the ForContext<T>() method.
- Call the logger.Copy
_logger.LogDebug(“Enabling agent {AgentName}”, Me.Name);
The following are examples of logging from an agent, a custom page, and an event handler.
Add custom metadata to loggers
In many cases, you may need to record additional details for your events, for example, to make them easier to identify. With Relativity you can add custom properties (metadata) to your logs. This can be done in the following ways:
- Create a logger scoped for a class to add the class properties to the metadata.Copy
var myClassLogger = _logger.ForContext<MyClass>();
- Pass an arbitrary string key-value pair to identify messages from a specific logger.Copy
var myLogger = _logger.ForContext(“CodeLocation”, “ZiggyStation”);
- Associate a code block with a logger through using statement. The following example demonstrates how to add a JobID property to the messages logged by the code running inside the using statement block.Copy
using (_logger.LogContextPushProperty("JobId", 12345))
{
_logger.LogWarning("Any usage of _logger
within this using context will now have the JobID property.");
}
Best practices
The following are the recommended best practices when using logging in your applications:
- Create a scoped logger for each class. See examples in Add loggers using API helpers.
- Always prefer structured data over string concatenation. Relativity logging events are associated with message templates.
Treating the string parameter to log methods as a message, as in the example below, will degrade performance and consume cache memory.Copy// Don't:
Relativity.Logging.Log.Logger.LogDebug("Enabling agent" + Me.Name);Instead, always use template properties to include variables in messages. Properties are passed in enclosed in braces:
Copy// Do:
Relativity.Logging.Log.Logger.LogDebug("Enabling agent {AgentName}", Me.Name); - If you have an object where all properties can be displayed in a log, you can use the @ symbol to declare a message param as a destructured object. Examples of properties that must never be displayed in the logs are passwords or decrypted text that is normally encrypted. A destructured object in a message template will deserialize the object into JSON and put the results in the message output and in the metadata.
This example shows what happens when you destructure a class object. Any objects that implement IEnumerable, like arrays, lists, tuples, dictionaries, etc., are destructured to JSON even without the @ symbol. Destructuring data types like strings or integers does not deserialize to a JSON string.
Copy//Sample class
namespace MySampleNamespace
{
public class MyObject
{
public string Description { get; set; }
public int Value { get; set; }
}
}
//Sample Logging Code
MyObject testObject = new MyObject()
{
Description = "This is my object",
Value = 100
};
//To get a destructured object put the @ symbol before the name of your message param name: {@...}
Relativity.Logging.Log.Logger.LogDebug("Testing MyObject destructured - {@DeserializedMyObject}", testObject);
//If you do not put the @ symbol the object will be outputted as if you did testObject.ToString()
Relativity.Logging.Log.Logger.LogDebug("Testing MyObject not destructured - {NonDeserializedMyObject}", testObject);
//Destructuring simple data types results in a non JSON serialized output, just like if
string myString = "My Test String";
Relativity.Logging.Log.Logger.LogDebug("Testing myString destructured - {@MyString}", myString);This is the output of the code:
CopyWith the @ symbol
Message: Testing MyObject destructured - MyObject { Description: "This is my object", Value: 100 }
Without the @ symbol
Message: Testing MyObject destructured - "MySampleNamespace.MyObject"
Simple data type with the @ symbol
Message: Testing myString destructured - "My Test String" - Log all exceptions by adding loggers to try-catch statements.
- Log all calls to Relativity REST services.
- Log all calls when using the service interfaces instantiated through ServiceFactory, for example, permissions and saved search APIs.
- Log any direct SQL access.
- Pay attention to the logging levels and don’t overuse logging. Logging can have a significant impact on your disk, database, processor, and network resources.