Question

I'm exploring adding TraceSource-based logging to my ASP.NET application. I need the ability to selectively control SourceLevels for different code components, hence the need for multiple sources. All TraceSource instances will write to a single FileLogTraceListener-derived listener.

Can this strategy create performance / concurrent access problems in a multi-threaded environment? From the MSDN descriptions, both TraceSource and FileLogTraceListener appear to be thread-safe. Does anyone have experience that indicates otherwise?

Is adding listeners via <sharedListers> in app.config / web.config preferred in this situation versus adding a listener programmatically as I've done in the code below?

I used this test code which does work perfectly - writes out the expected number of log entries. Just wanted some guidance before I deploy this strategy in production.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.VisualBasic.Logging;

namespace SourcesListeners
{
    class Program
    {
        [STAThread]
        static void Main()
        {
            const string baseFileName = @"test-log";
            var threads = 10*Environment.ProcessorCount;
            const int iterationsPerThread = 4000;

            var listener = new DailyRollingFileListener(@".\", baseFileName);
            {
                Parallel.For(0, threads, i =>
                {
                    var source = new TraceSource(string.Format("source-{0}", i), SourceLevels.All);
                    source.Listeners.Clear();
                    source.Listeners.Add(listener);
                    source.TraceEvent(TraceEventType.Information, 0, "Created");

                    for (var k = 0; k < iterationsPerThread; ++k)
                    {
                        source.TraceEvent(TraceEventType.Information, 0, "thread: {0}, iteration: {1}", i, k);
                    }
                });
            }
        }

        class DailyRollingFileListener : FileLogTraceListener
        {
            public DailyRollingFileListener(
                string customLocation, string baseFileName,
                bool autoFlush = true)
            {
                CustomLocation = customLocation;
                BaseFileName = baseFileName;
                AutoFlush = autoFlush;
                LogFileCreationSchedule = LogFileCreationScheduleOption.Daily;
                Append = false;
                MaxFileSize = 40*1024*1024;
            }

            public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
            {
                var entry = string.Format("{0:yyyy-MM-dd hh:mm:ss.fff}Z {1,4} {2,-5} {3} {4}",
                    eventCache.DateTime,
                    eventCache.ThreadId,
                    GetSeverity(eventType),
                    source,
                    message);
                base.WriteLine(entry);
            }

            public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
            {
                var message = args != null ? string.Format(format, args) : format;
                if (eventCache != null)
                {
                    TraceEvent(eventCache, source ?? string.Empty, eventType, id, message);
                }
                else
                {
                    base.WriteLine(string.Format("{0} {1} {2}", GetSeverity(eventType), source ?? string.Empty, message));
                }
            }

            private static string GetSeverity(TraceEventType eventType)
            {
                string value;
                return SeverityLevel.TryGetValue(eventType, out value) ? value : eventType.ToString().ToUpper();
            }

            private static readonly Dictionary<TraceEventType, string> SeverityLevel =
                new Dictionary<TraceEventType, string>
            {
                {TraceEventType.Critical, "FATAL"},
                {TraceEventType.Error, "ERROR"},
                {TraceEventType.Warning, "WARN "},
                {TraceEventType.Information, ""},
                {TraceEventType.Verbose, "DEBUG"},
                {TraceEventType.Start, "ENTRY"},
                {TraceEventType.Stop, "EXIT "},
            };
        }
    }
}
Was it helpful?

Solution

FileLogTraceListener will write to a brand new file if the existing file is locked. So you can get a collection of log files, which may be what you want, or maybe not.

Databases are more resilient to concurrent access, but you can run into deadlock, especially if there are transactions going on (and everyone is trying to write to the same table)

OTHER TIPS

To avoid creating multiple trace files you can dispose the listener after flush to release the trace file.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top