<commit_before>﻿using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Security.Authentication;
using System.Text;
using Griffin.Net.Protocols.Http;
using ITCC.HTTP.Enums;

namespace ITCC.HTTP.Server
{
    internal class ServerStatistics<TAccount>
        where TAccount : class
    {
        private readonly ConcurrentDictionary<int, int> _responseCodes = new ConcurrentDictionary<int, int>();

        private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, int>> _requestMethodCounters = new ConcurrentDictionary<string, ConcurrentDictionary<string, int>>();

        private readonly ConcurrentDictionary<string, int> _requestSuccessCounters = new ConcurrentDictionary<string, int>();

        private readonly ConcurrentDictionary<string, int> _requestFailCounters = new ConcurrentDictionary<string, int>();

        private readonly ConcurrentDictionary<string, double> _requestSuccessTimeCounters = new ConcurrentDictionary<string, double>();

        private readonly ConcurrentDictionary<string, double> _requestFailTimeCounters = new ConcurrentDictionary<string, double>();

        private readonly ConcurrentDictionary<string, int> _legacyRequestCounter = new ConcurrentDictionary<string, int>();

        private readonly ConcurrentDictionary<SslProtocols, int> _sslProtocolCounter = new ConcurrentDictionary<SslProtocols, int>(); 

        private readonly ConcurrentDictionary<AuthorizationStatus, int> _authentificationResults = new ConcurrentDictionary<AuthorizationStatus, int>();

        private readonly DateTime _startTime = DateTime.Now;

        /// <summary>
        ///     In milliseconds
        /// </summary>
        private double _minRequestTime = double.PositiveInfinity;

        /// <summary>
        ///     In milliseconds
        /// </summary>
        private double _maxRequestTime = double.NegativeInfinity;

        /// <summary>
        ///     In milliseconds
        /// </summary>
        private double _totalRequestTime;

        private string _slowestRequest;

        private long _requestCount;

        public string Serialize()
        {
            var builder = new StringBuilder();

            builder.AppendLine($"Server started at {_startTime.ToString("s")} ({DateTime.Now.Subtract(_startTime)} ago)");
            builder.AppendLine();

            builder.AppendLine("Legacy request code statistics:");
            var legacyKeys = _legacyRequestCounter.Keys.ToList();
            legacyKeys.Sort();
            legacyKeys.ForEach(k => builder.AppendLine($"\t{k}: {_legacyRequestCounter[k]}"));
            builder.AppendLine();

            builder.AppendLine("Response code statistics:");
            var keys = _responseCodes.Keys.ToList();
            keys.Sort();
            keys.ForEach(k => builder.AppendLine($"\t{k}: {_responseCodes[k]}"));
            builder.AppendLine();

            builder.AppendLine("Authcode statistics:");
            var authKeys = _authentificationResults.Keys.ToList();
            authKeys.Sort();
            authKeys.ForEach(k => builder.AppendLine($"\t{k, 12}: {_authentificationResults[k]}"));
            builder.AppendLine();

            builder.AppendLine("SSL statistics:");
            var sslKeys = _sslProtocolCounter.Keys.ToList();
            sslKeys.Sort();
            sslKeys.ForEach(k => builder.AppendLine($"\t{k,12}: {_sslProtocolCounter[k]}"));
            builder.AppendLine();

            builder.AppendLine("Request statistics:");
            var uris = _requestMethodCounters.Keys.ToList();
            uris.Sort();
            uris.ForEach(u =>
            {
                var methodDict = _requestMethodCounters[u];
                double totalSuccessTime;
                double totalFailTime;
                if (!_requestSuccessTimeCounters.TryGetValue(u, out totalSuccessTime))
                    totalSuccessTime = 0;
                if (!_requestFailTimeCounters.TryGetValue(u, out totalFailTime))
                    totalFailTime = 0;
                double averageSuccessTime;
                double averageFailTime;
                if (_requestSuccessCounters.ContainsKey(u))
                {
                    averageSuccessTime = totalSuccessTime/_requestSuccessCounters[u];
                }
                else
                {
                    averageSuccessTime = 0;
                }

                if (_requestFailCounters.ContainsKey(u))
                {
                    averageFailTime = totalFailTime / _requestFailCounters[u];
                }
                else
                {
                    averageFailTime = 0;
                }

                builder.AppendLine($"\t{u}");
                builder.AppendLine($"\t\tAverage success time (2xx):      {averageSuccessTime,10} ms");
                builder.AppendLine($"\t\tAverage fail    time (4xx, 5xx): {averageFailTime,10} ms");

                var methodKeys = methodDict.Keys.ToList();
                methodKeys.Sort();
                methodKeys.ForEach(m => builder.AppendLine($"\t\t{m, -10}: { methodDict[m]}"));
            });
            builder.AppendLine();

            if (_requestCount > 0)
            {
                builder.AppendLine("Total performance statistics:");
                builder.AppendLine($"\tRequest count:        {_requestCount}");
                builder.AppendLine($"\tAverage request time: {_totalRequestTime / _requestCount, 10} ms");
                builder.AppendLine($"\tMax     request time: {_maxRequestTime, 10} ms");
                builder.AppendLine($"\tMin     request time: {_minRequestTime, 10} ms");
                builder.AppendLine($"\tTotal   request time: {_totalRequestTime, 10} ms");
                builder.AppendLine($"\tSlowest request:      {_slowestRequest}");
            }
            

            return builder.ToString();
        }

        public void AddResponse(HttpResponse response, string uri, double processingTime)
        {
            InitOrIncrement(_responseCodes, response.StatusCode);

            _minRequestTime = Math.Min(processingTime, _minRequestTime);
            if (processingTime > _maxRequestTime)
            {
                _maxRequestTime = processingTime;
                _slowestRequest = uri;
            }
            _totalRequestTime += processingTime;
            _requestCount++;

            if (HasGoodStatusCode(response))
            {
                if (!_requestSuccessTimeCounters.ContainsKey(uri))
                {
                    _requestSuccessTimeCounters[uri] = processingTime;
                }
                else
                {
                    _requestSuccessTimeCounters[uri] += processingTime;
                }<commit_msg>Use _counterLock to prevent concurrency issues in statistics<commit_after>﻿using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Security.Authentication;
using System.Text;
using Griffin.Net.Protocols.Http;
using ITCC.HTTP.Enums;

namespace ITCC.HTTP.Server
{
    internal class ServerStatistics<TAccount>
        where TAccount : class
    {
        private readonly ConcurrentDictionary<int, int> _responseCodes = new ConcurrentDictionary<int, int>();

        private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, int>> _requestMethodCounters = new ConcurrentDictionary<string, ConcurrentDictionary<string, int>>();

        private readonly ConcurrentDictionary<string, int> _requestSuccessCounters = new ConcurrentDictionary<string, int>();

        private readonly ConcurrentDictionary<string, int> _requestFailCounters = new ConcurrentDictionary<string, int>();

        private readonly ConcurrentDictionary<string, double> _requestSuccessTimeCounters = new ConcurrentDictionary<string, double>();

        private readonly ConcurrentDictionary<string, double> _requestFailTimeCounters = new ConcurrentDictionary<string, double>();

        private readonly ConcurrentDictionary<string, int> _legacyRequestCounter = new ConcurrentDictionary<string, int>();

        private readonly ConcurrentDictionary<SslProtocols, int> _sslProtocolCounter = new ConcurrentDictionary<SslProtocols, int>(); 

        private readonly ConcurrentDictionary<AuthorizationStatus, int> _authentificationResults = new ConcurrentDictionary<AuthorizationStatus, int>();

        private readonly DateTime _startTime = DateTime.Now;

        private readonly object _counterLock = new object();

        /// <summary>
        ///     In milliseconds
        /// </summary>
        private double _minRequestTime = double.PositiveInfinity;

        /// <summary>
        ///     In milliseconds
        /// </summary>
        private double _maxRequestTime = double.NegativeInfinity;

        /// <summary>
        ///     In milliseconds
        /// </summary>
        private double _totalRequestTime;

        private string _slowestRequest;

        private long _requestCount;

        public string Serialize()
        {
            var builder = new StringBuilder();

            builder.AppendLine($"Server started at {_startTime.ToString("s")} ({DateTime.Now.Subtract(_startTime)} ago)");
            builder.AppendLine();

            builder.AppendLine("Legacy request code statistics:");
            var legacyKeys = _legacyRequestCounter.Keys.ToList();
            legacyKeys.Sort();
            legacyKeys.ForEach(k => builder.AppendLine($"\t{k}: {_legacyRequestCounter[k]}"));
            builder.AppendLine();

            builder.AppendLine("Response code statistics:");
            var keys = _responseCodes.Keys.ToList();
            keys.Sort();
            keys.ForEach(k => builder.AppendLine($"\t{k}: {_responseCodes[k]}"));
            builder.AppendLine();

            builder.AppendLine("Authcode statistics:");
            var authKeys = _authentificationResults.Keys.ToList();
            authKeys.Sort();
            authKeys.ForEach(k => builder.AppendLine($"\t{k, 12}: {_authentificationResults[k]}"));
            builder.AppendLine();

            builder.AppendLine("SSL statistics:");
            var sslKeys = _sslProtocolCounter.Keys.ToList();
            sslKeys.Sort();
            sslKeys.ForEach(k => builder.AppendLine($"\t{k,12}: {_sslProtocolCounter[k]}"));
            builder.AppendLine();

            builder.AppendLine("Request statistics:");
            var uris = _requestMethodCounters.Keys.ToList();
            uris.Sort();
            uris.ForEach(u =>
            {
                var methodDict = _requestMethodCounters[u];
                double totalSuccessTime;
                double totalFailTime;
                if (!_requestSuccessTimeCounters.TryGetValue(u, out totalSuccessTime))
                    totalSuccessTime = 0;
                if (!_requestFailTimeCounters.TryGetValue(u, out totalFailTime))
                    totalFailTime = 0;
                double averageSuccessTime;
                double averageFailTime;
                if (_requestSuccessCounters.ContainsKey(u))
                {
                    averageSuccessTime = totalSuccessTime/_requestSuccessCounters[u];
                }
                else
                {
                    averageSuccessTime = 0;
                }

                if (_requestFailCounters.ContainsKey(u))
                {
                    averageFailTime = totalFailTime / _requestFailCounters[u];
                }
                else
                {
                    averageFailTime = 0;
                }

                builder.AppendLine($"\t{u}");
                builder.AppendLine($"\t\tAverage success time (2xx):      {averageSuccessTime,10} ms");
                builder.AppendLine($"\t\tAverage fail    time (4xx, 5xx): {averageFailTime,10} ms");

                var methodKeys = methodDict.Keys.ToList();
                methodKeys.Sort();
                methodKeys.ForEach(m => builder.AppendLine($"\t\t{m, -10}: { methodDict[m]}"));
            });
            builder.AppendLine();

            if (_requestCount > 0)
            {
                builder.AppendLine("Total performance statistics:");
                builder.AppendLine($"\tRequest count:        {_requestCount}");
                builder.AppendLine($"\tAverage request time: {_totalRequestTime / _requestCount, 10} ms");
                builder.AppendLine($"\tMax     request time: {_maxRequestTime, 10} ms");
                builder.AppendLine($"\tMin     request time: {_minRequestTime, 10} ms");
                builder.AppendLine($"\tTotal   request time: {_totalRequestTime, 10} ms");
                builder.AppendLine($"\tSlowest request:      {_slowestRequest}");
            }
            

            return builder.ToString();
        }

        public void AddResponse(HttpResponse response, string uri, double processingTime)
        {
            InitOrIncrement(_responseCodes, response.StatusCode);

            lock (_counterLock)
            {
                _minRequestTime = Math.Min(processingTime, _minRequestTime);
                if (processingTime > _maxRequestTime)
                {
                    _maxRequestTime = processingTime;
                    _slowestRequest = uri;
                }
                _totalRequestTime += processingTime;
                _requestCount++;
            }

            if (HasGoodStatusCode(response))
            {
                if (!_requestSuccessTimeCounters.ContainsKey(uri))
                {
                    _requestSuccessTimeCounters[uri] = processingTime;
                }
                else
                {
                    _requestSuccessTimeCounters[uri] += processingTime;
                }
{'fraction_non_alphanumeric': 0.10016791998247791, 'fraction_numerical': 0.0037964517777615537, 'mean_word_length': 2.071300448430493, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3381143', 'n_tokens_mistral': 3433, 'n_tokens_neox': 3226, 'n_words': 669}