{"id":10057,"date":"2026-02-05T16:48:16","date_gmt":"2026-02-05T15:48:16","guid":{"rendered":"https:\/\/solidt.eu\/site\/?p=10057"},"modified":"2026-02-05T16:48:18","modified_gmt":"2026-02-05T15:48:18","slug":"c-log4net-asyncrollingfileappender","status":"publish","type":"post","link":"https:\/\/solidt.eu\/site\/c-log4net-asyncrollingfileappender\/","title":{"rendered":"C# log4net AsyncRollingFileAppender"},"content":{"rendered":"\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"csharp\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">using System;\nusing System.Collections.Concurrent;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Text;\nusing System.Threading;\nusing log4net.Appender;\nusing log4net.Core;\nusing log4net.Layout;\n\n\nnamespace Logging\n{\n    public sealed class AsyncRollingFileAppender : AppenderSkeleton, IFlushable\n    {\n        private BlockingCollection&lt;string> _queue;\n        private Thread _worker;\n        private CancellationTokenSource _cts;\n\n        private FileStream _stream;\n        private StreamWriter _writer;\n\n        private long _currentSize;\n\n        private string _fullPath = null;\n        private string _directory = null;\n\n        public string FileName { get; set; }\n        public int MaxSizeRollBackups { get; set; } = 5;\n        public long MaximumFileSizeBytes { get; set; } = 20 * 1024 * 1024;\n        public TimeSpan IdleFlushInterval { get; set; } = TimeSpan.FromSeconds(5);\n        public int BufferSize { get; set; } = 10000;\n\n        public override void ActivateOptions()\n        {\n            base.ActivateOptions();\n\n            if (Layout == null)\n                Layout = new PatternLayout(\"%date %-5level %message%newline\");\n\n            if (_stream != null)\n            {\n                OnClose();\n            }\n\n            _queue = new BlockingCollection&lt;string>(BufferSize);\n            _cts = new CancellationTokenSource();\n\n            _fullPath = ResolveFilePath(FileName);\n            _directory = Path.GetDirectoryName(_fullPath);\n            Directory.CreateDirectory(_directory);\n\n            OpenFile();\n\n            _worker = new Thread(WorkerLoop)\n            {\n                IsBackground = true,\n                Name = \"AsyncRollingFileAppender\"\n            };\n            _worker.Start();\n        }\n\n        protected override void Append(LoggingEvent loggingEvent)\n        {\n            loggingEvent.Fix = FixFlags.All;\n\n            var rendered = RenderLoggingEvent(loggingEvent);\n            _queue.TryAdd(rendered);\n        }\n\n        private void WorkerLoop()\n        {\n            try\n            {\n                while (!_cts.IsCancellationRequested)\n                {\n                    if (_queue.TryTake(out var line, IdleFlushInterval))\n                    {\n                        WriteInternal(line);\n                    }\n                    else\n                    {\n                        FlushInternal();\n                    }\n                }\n            }\n            catch\n            {\n                \/\/ logging mag nooit exceptions naar buiten gooien\n            }\n        }\n\n        private void WriteInternal(string text)\n        {\n            var bytes = Encoding.UTF8.GetByteCount(text);\n\n            if (_currentSize + bytes > MaximumFileSizeBytes)\n                RollFile();\n\n            _writer.Write(text);\n            _currentSize += bytes;\n        }\n\n        private void FlushInternal()\n        {\n            _writer?.Flush();\n            _stream?.Flush(true);\n        }\n\n        private static string ResolveFilePath(string fileName)\n        {\n            if (string.IsNullOrWhiteSpace(fileName))\n                throw new ArgumentException(\"File name must be set\");\n\n            \/\/ 1. Environment variables (%TEMP%, %APPDATA%, etc.)\n            var expanded = Environment.ExpandEnvironmentVariables(fileName);\n\n            \/\/ 2. Absolute path maken (relatief \u2192 base directory)\n            var fullPath = Path.GetFullPath(expanded);\n\n            \/\/ 3. Directory bepalen\n            var directory = Path.GetDirectoryName(fullPath);\n\n            \/\/ 4. Alleen directory aanmaken als die bestaat\n            if (!string.IsNullOrEmpty(directory))\n            {\n                Directory.CreateDirectory(directory);\n            }\n\n            return fullPath;\n        }\n\n        private void OpenFile()\n        {\n            var fullPath = ResolveFilePath(FileName);\n            Directory.CreateDirectory(Path.GetDirectoryName(fullPath));\n\n            _stream = new FileStream(\n                fullPath,\n                FileMode.Append,\n                FileAccess.Write,\n                FileShare.Read,\n                4096,\n                FileOptions.SequentialScan);\n\n            _writer = new StreamWriter(_stream, Encoding.UTF8)\n            {\n                AutoFlush = false\n            };\n\n            _currentSize = _stream.Length;\n        }\n\n        private void RollFile()\n        {\n            FlushInternal();\n            _writer.Dispose();\n            _stream.Dispose();\n\n            var baseFileName = Path.GetFileName(_fullPath);\n\n            for (int i = MaxSizeRollBackups - 1; i >= 1; i--)\n            {\n                var src = Path.Combine(_directory, $\"{baseFileName}.{i}\");\n                var dst = Path.Combine(_directory, $\"{baseFileName}.{i + 1}\");\n                if (File.Exists(src))\n                {\n                    if (File.Exists(dst)) File.Delete(dst);\n                    File.Move(src, dst);\n                }\n            }\n\n            if (MaxSizeRollBackups > 0)\n            {\n                var first = Path.Combine(_directory, $\"{baseFileName}.1\");\n                if (File.Exists(first)) File.Delete(first);\n                File.Move(_fullPath, first);\n            }\n\n            OpenFile();\n        }\n\n        protected override void OnClose()\n        {\n            Flush(5000);\n\n            _cts.Cancel();\n            _queue.CompleteAdding();\n            _worker.Join(5000);\n\n            _writer?.Dispose();\n            _stream?.Dispose();\n\n            base.OnClose();\n        }\n\n        bool IFlushable.Flush(int millisecondsTimeout)\n        {\n            var sw = Stopwatch.StartNew();\n            while (_queue.Count > 0 &amp;&amp; sw.ElapsedMilliseconds &lt; millisecondsTimeout)\n            {\n                Thread.Sleep(10);\n            }\n\n            FlushInternal();\n            return _queue.Count == 0;\n        }\n    }\n}\n<\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-10057","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/10057","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/comments?post=10057"}],"version-history":[{"count":1,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/10057\/revisions"}],"predecessor-version":[{"id":10058,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/10057\/revisions\/10058"}],"wp:attachment":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/media?parent=10057"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/categories?post=10057"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/tags?post=10057"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}