public void LogMetrics()
        {
            if (_currentTableFileName == "")
            {
                return;
            }

            while (!created)
            {
                Thread.Sleep(2);
            }

            var nowSinceEpoch = _nowSinceEpoch;
            var timeString    = "" + (long)nowSinceEpoch.TotalMilliseconds;

            var playerIds = _playerData.GetKeys();

            var nowTurn   = Time.CurrentTurn;
            var nowEvents = _eventMap.GetEventsForTurn(nowTurn);

            Polygon[] nowPolygons = new Polygon[playerIds.Count];
            foreach (TimedEvent nowEvent in nowEvents)
            {
                TimedArea nowArea = null;
                if (nowEvent.EventType == EventType.Area)
                {
                    nowArea = nowEvent as TimedArea;
                }
                else if (nowEvent.EventType == EventType.Transition)
                {
                    Transition      nowTransition = (Transition)nowEvent;
                    TransitionFrame nowFrame      =
                        nowTransition.Frames.Find(frame => frame.Area != null && frame.Area.IsActiveAt(nowTurn));

                    if (nowFrame == null)
                    {
                        continue;
                    }
                    nowArea = nowFrame.Area;
                }

                if (nowArea == null)
                {
                    continue;
                }
                var playerIndex = playerIds.IndexOf(nowEvent.PlayerId);
                if (playerIndex < 0)
                {
                    continue;
                }
                nowPolygons[playerIndex] = nowArea.Area;
            }

            for (int playerIndex = 0; playerIndex < playerIds.Count; playerIndex++)
            {
                if (nowPolygons[playerIndex] == null)
                {
                    nowPolygons[playerIndex] = new Polygon();
                }
            }

            Vector[] nowPositions = new Vector[playerIds.Count];
            // append empty when player is not logged in
            for (int playerNum = 0; playerNum < playerIds.Count; playerNum++)
            {
                var playerId = playerIds[playerNum];

                if (_playerData.TryGetEntry(playerId, out PlayerDataEntry player))
                {
                    var position = player.Position;
                    nowPositions[playerNum] = position;
                }
            }

            UpdateBreaches(playerIds, nowPositions, nowPolygons,
                           out float[] minimalDistanceToOther, out bool[] isBreaching, out double[] minDistPerUser, out double[] maxDistPerUser);

            // appending to file
            PrepareAppend();

            for (int userNum = 0; userNum < playerIds.Count; userNum++)
            {
                AppendSingle(timeString);

                var playerId = playerIds[userNum];

                AppendSingle($"{playerId}");

                var position = nowPositions[userNum];

                if (position == null)
                {
                    AppendSingle();
                    AppendSingle();
                }
                else
                {
                    AppendSingle(position.X.ToString("0.000"));
                    AppendSingle(position.Z.ToString("0.000"));
                }

                var playerPolygon = nowPolygons[userNum];
                var playerPoints  = playerPolygon.Points;
                for (int nodeNum = 0; nodeNum < MaxPolyPoints; nodeNum++)
                {
                    if (nodeNum < playerPoints.Count && playerPoints[nodeNum] != null)
                    {
                        var nodePosition = playerPoints[nodeNum];
                        AppendSingle(nodePosition.X.ToString("0.000"));
                        AppendSingle(nodePosition.Z.ToString("0.000"));
                    }
                    else
                    {
                        AppendSingle();
                        AppendSingle();
                    }
                }

                AppendSingle(isBreaching[userNum] ? "1" : "0");
                AppendSingle(minimalDistanceToOther[userNum] > 10 ? "" : minimalDistanceToOther[userNum].ToString("0.000"));

                AppendSingle(minDistPerUser[userNum] > 10 ? "" : minDistPerUser[userNum].ToString("0.000"));
                AppendSingle(maxDistPerUser[userNum] > 10 ? "" : maxDistPerUser[userNum].ToString("0.000"), userNum == MaxPlayers - 1 ? false : true);

                AppendSingle(seperator: false, newLine: true);
            }

            FinalizeAppend();
        }