Multiple Instances of Log Files?

Feb 15, 2010 at 12:19 PM

Hello,

I was looking at a slimmer way and more flexible way of logging in an application that has multiple instances running.  Ideally, it would just tack perhaps the ProcessID on the end of the file name.  Can this framework do that programmatically.  Ideally I would like to set the File name to get logged to in the actualy program.

Thanks,
Kevin

Coordinator
Feb 15, 2010 at 2:15 PM

Hi Kevin,

Thank you for your interest in CuttingEdge.Logging.

Logging process ids not supported out of the box, but there are several ways to achieve this.

First of all, when you want to write to a file, you can look at the FileLoggingProvider example at the home page. The current release of CuttingEdge.Logging lacks file logger, but I’m considering this for a coming release. However, till this moment, nobody has requested it.

When you want to log the process id using the FileLoggingProvider, just change the LogInternal method to the following:

 

protected override object LogInternal(LogEntry entry)
{
    DateTime now = DateTime.Now;
    int currentProcessId = Process.GetCurrentProcess().Id;
    
    string line = String.Format(
        "{5}\t{0} {1}\t{2}\t{3}\t{4}\n",
        now.ToShortDateString(),
        now.ToShortTimeString(),
        entry.Severity,
        entry.Message,
        entry.Exception,
        currentProcessId);
    
    File.AppendAllText(this.path, line);
    return null;
}

When you want to log messages from multiple web applications to a single SQL Server database, you can already use the AspNetSqlLoggingProvider. It can be configured with a unique 'applicationName' attribute per web application in its configuration file, which will be sent to the database.

When you're not using web applications, you probably need to extend one of the existing logging providers in such a way that they include the process id in the source information. Look at the following example as it extends the MailLoggingProvider:

public class ProcessIdMailLoggingProvider : MailLoggingProvider
{
    private static readonly int currentProcessId = Process.GetCurrentProcess().Id;

    protected override object LogInternal(LogEntry entry)
    {
        var entryWithProcessId = AddProcessId(entry);
        return base.LogInternal(entryWithProcessId);
    }

    private static LogEntry AddProcessId(LogEntry entry)
    {
        var id = "ProcessId: " + currentProcessId.ToString();
        var sourceWithProcessId =
            String.IsNullOrEmpty(entry.Source) ? id : id + ", " + entry.Source;
        return new LogEntry(entry.Severity, entry.Message, sourceWithProcessId, entry.Exception);
    }
}

Overriding the LogInternal method works with all providers.

You can also extend the SqlLoggingProvider in such way that it injects the process id into a separate column in your database. For this you need to change the default schema a bit, by adding a new column to the dbo.logging_events table and changing the dbo.logging_AddEvent procedure. You also need to extend the SqlLoggingProvider so that it sends the process id to the logging_AddEvent procedure. Here's the code:

public class ProcessIdSqlLoggingProvider : SqlLoggingProvider
{
    private static readonly int currentProcessId = Process.GetCurrentProcess().Id;

    protected override int SaveEventToDatabase(SqlTransaction transaction,
        LoggingEventType severity, string message, string source)
    {
        using (SqlCommand command = new SqlCommand("dbo.logging_AddEvent", transaction.Connection,
            transaction))
        {
            command.CommandType = CommandType.StoredProcedure;
            AddParameter(command, "EventTypeId", SqlDbType.Int, (int)severity);
            AddParameter(command, "Message", SqlDbType.NText, message);
            AddParameter(command, "Source", SqlDbType.NText, source);
            AddParameter(command, "ProcessId", SqlDbType.Int, currentProcessId);
            return (int)command.ExecuteScalar();
        }
    }

    private static SqlParameter AddParameter(SqlCommand command, string parameterName,
        SqlDbType type, object value)
    {
        SqlParameter parameter = command.CreateParameter();
        parameter.IsNullable = true;
        parameter.SqlDbType = type;
        parameter.ParameterName = parameterName;
        parameter.Value = value ?? DBNull.Value;
        command.Parameters.Add(parameter);
        return parameter;
    }
}
I hope this helps.