This is the online documentation for PostSharp 5.0.
Download PDF or CHM. Go to v4.3 or v5.0

Adding Custom Log Records Manually

When you're using the LogAttribute aspect, PostSharp automatically generates code that emits log records before and after the execution of a method. But there are times when you will want to write your own records. For instance, you may want to log a custom error or warning. You may want this message to be displayed even when trace-level logging is disabled. But when it is enabled, you want this message to appear in the right context, with the proper indentation.

For these scenarios, you can use the methods provided by the Logger class.

This topic contains the following sections:

Emitting custom log records

To write a custom log record, use the Logger.Write(LogLevel, String) method.

To write custom log records:

  1. Define a static field of type Logger in the class emitting the log records and initialize it using the Logger.GetLogger(String) method.

  2. Emit log records using the Logger.Write(LogLevel, String) method.

Example

C#
static class Hasher
{
    static readonly Logger logger = Logger.GetLogger();

    public static async Task ReadAndHashAsync(string url)
    {
       if ( string.IsNullOrEmpty( url ) )
       {
          logger.Write(LogLevel.Warning, "Empty URL passed. Skipping this method.");
          return;
       }

       // Details skipped.
    }
}
Logging records with parameters

Most of the time, you won't log constant strings, but you will want to include variable pieces of information. In this case, you can use one of the overloads of the Logger.Write(LogLevel, String) that accepts formatting parameters.

Note that the specification of the formatting string used by the Logger class is not identical to the one used by string.Format.

The formatting string used by the Logger class is designed to support named parameters (for use with logging back-ends that support it, i.e. semantic logging) and for high-performance evaluation. It has the following specifications:

  • Named parameters must be surrounded by curly brackets, e.g. {MyParameter}.

  • Values are matched to named parameters by position. This means that the order of named parameters in the formatting string must match the order of corresponding values passed to the Logger.Write(LogLevel, String) method and that two named parameters with the same name are not matched to the same value.

  • Anything inside a pair of curly brackets is considered as the parameter name, and will be passed to the back-end as it, without further parsing.

  • Formatting specifiers are not supported but may be partially supported in the future. Do not use colons (:) in your parameter names, as they may be interpreted differently in future versions of PostSharp.

  • Use the escaped form of curly brackets {{ and }} if you want to include curly brackets in the formatted string.

Important note Important

Consider the performance impact of using string.Format or equivalent constructs such as interpolated strings. PostSharp Logging is highly optimized and is able to generate a logging record without allocating any memory on the heap. If you're using a high-performance back-end, using string.Format can bring a significant performance overhead to your logging.

Example

C#
static class Hasher
{
    static readonly Logger logger = Logger.GetLogger();

    public static byte[] ReadAndHash(string url)
    {
        var hashAlgorithm = HashAlgorithm.Create("SHA256");
        hashAlgorithm.Initialize();

        var webClient = new WebClient();
        var buffer = new byte[16 * 1024];

        logger.Write(LogLevel.Info, "Using a {BufferSize}-byte buffer.", buffer.Length);

        using (var stream = webClient.OpenRead(url))
        {
            int countRead;
            while ((countRead = stream.Read(buffer, 0, buffer.Length)) != 0)
            {
                logger.Write(LogLevel.Info, "Got {CountRead} bytes.", countRead);
                hashAlgorithm.ComputeHash(buffer, 0, countRead);
            }
        }

        return hashAlgorithm.Hash;
    }
}
Defining custom activities

More often than not, you will find yourself logging the beginning and the end of an activity, e.g. Starting to read MyFile.xml and Succeeded to read MyFile.xml or Cannot read MyFile.xml: unexpected element at line 5. With PostSharp, this concept is represented by the LogActivity class. An activity is anything that starts and stops. When you use the LogAttribute aspect to log a method, it also translates into an activity behind the scenes. Activities are hierarchical: they have a notion of child and parent and form a tree. Activities can be synchronous and asynchronous.

To define a custom activity:

  1. If you haven't already done it, define a static field of type Logger in the class emitting the log records and initialize it using the Logger.GetLogger(String) method.

  2. To start the activity, call the Logger.OpenActivity(String) method, or the Logger.OpenAsyncActivity(String) method if your method is async.

  3. To close the activity, call the SetSuccess, SetFailure or SetException method. We suggest that you wrap the activity source code inside a try block and call the SetException method from the catch block.

Example

C#
static class Hasher
{
    static readonly Logger logger = Logger.GetLogger();

    [Log]
    private static async Task<byte[]> ReadAndHashAsync(string file)
    {
        var activity = logger.OpenAsyncActivity("Processing file {Url}", file);

        try
        {
            var totalSize = 0;

            var hashAlgorithm = HashAlgorithm.Create("SHA256");
            hashAlgorithm.Initialize();

            var buffer = new byte[128 * 1024];

            activity.Write(LogLevel.Info, "Working with a {BufferSize}-byte buffer.", buffer.Length);

            using (var stream = File.OpenRead(file))
            {
                int countRead;
                while ((countRead = await stream.ReadAsync(buffer, 0, buffer.Length)) != 0)
                {
                    activity.Write(LogLevel.Info, "Got {CountRead} bytes.", countRead);

                    hashAlgorithm.ComputeHash(buffer, 0, countRead);
                    totalSize += countRead;
                }
            }


            activity.SetSuccess("Success. Read {TotalSize} bytes in total.", totalSize);
            return hashAlgorithm.Hash;
        }
        catch ( Exception e )
        {
            activity.SetException(e);
            throw;
        }

    }
}
Changing the severity level of custom activities

By default, entry and success messages of activities are logged with Debug severity and failures and exceptions with Error severity.

You can change the default severity by setting the properties of the DiagnosticsCustomRecordLoggingOptions class by using the following code:

LoggingServices.Roles[LoggingRoles.Custom].CustomRecordLoggingOptions.ActivityLevel = LogLevel.Trace;

In the code above, the LoggingRoles.Custom string refers to the role parameter of the GetLogger(String) method, whose default value equals to LoggingRoles.Custom.