<commit_before>﻿using Discord;
using System.Reflection;
using System.Text;

namespace RegexBot.Services.Logging;
/// <summary>
/// Implements program-wide logging.
/// </summary>
class LoggingService : Service {
    // NOTE: Service.Log's functionality is implemented here. DO NOT use within this class.
    private readonly string? _logBasePath;

    internal LoggingService(RegexbotClient bot) : base(bot) {
        _logBasePath = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)
            + Path.DirectorySeparatorChar + "logs";
        try {
            if (!Directory.Exists(_logBasePath)) Directory.CreateDirectory(_logBasePath);
            Directory.GetFiles(_logBasePath);
        } catch (Exception ex) when (ex is IOException or UnauthorizedAccessException) {
            _logBasePath = null;
            DoLog(Name, "Cannot create or access logging directory. File logging will be disabled.");
        }

        bot.DiscordClient.Log += DiscordClient_Log;
    }

    /// <summary>
    /// Discord.Net logging events handled here.
    /// Only events with high importance are stored. Others are just printed to console.
    /// </summary>
    private Task DiscordClient_Log(LogMessage arg) {
        var msg = $"[{Enum.GetName(typeof(LogSeverity), arg.Severity)}] {arg.Message}";
        if (arg.Exception != null) msg += arg.Exception.ToString();

        switch (arg.Message) { // Prevent webhook logs for these 'important' Discord.Net messages
            case "Connecting":
            case "Connected":
            case "Ready":
            case "Disconnecting":
            case "Disconnected":
            case "Resumed previous session":
            case "Failed to resume previous session":
                break;
        }
        DoLog("Discord.Net", msg);

        return Task.CompletedTask;
    }

    // Hooked
    internal void DoLog(string source, string? message) {
        message ??= "(null)";
        var now = DateTimeOffset.UtcNow;
        var output = new StringBuilder();
        var prefix = $"[{now:u}] [{source}] ";
        foreach (var line in message.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None)) {
            output.Append(prefix).AppendLine(line);
        }
        var outstr = output.ToString();
        Console.Write(outstr);
        if (_logBasePath != null) {
            var filename = _logBasePath + Path.DirectorySeparatorChar + $"{now:yyyy-MM}.log";
            File.AppendAllText(filename, outstr, Encoding.UTF8);
        }
    }
}<commit_msg>Switch logging to local time<commit_after>﻿using Discord;
using System.Reflection;
using System.Text;

namespace RegexBot.Services.Logging;
/// <summary>
/// Implements program-wide logging.
/// </summary>
class LoggingService : Service {
    // NOTE: Service.Log's functionality is implemented here. DO NOT use within this class.
    private readonly string _logBasePath;

    internal LoggingService(RegexbotClient bot) : base(bot) {
        _logBasePath = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)
            + Path.DirectorySeparatorChar + "logs";
        try {
            if (!Directory.Exists(_logBasePath)) Directory.CreateDirectory(_logBasePath);
            Directory.GetFiles(_logBasePath);
        } catch (Exception ex) when (ex is IOException or UnauthorizedAccessException) {
            throw new Exception("Cannot create or access logging directory.");
        }

        bot.DiscordClient.Log += DiscordClient_Log;
    }

    /// <summary>
    /// Discord.Net logging events handled here.
    /// Only events with high importance are stored. Others are just printed to console.
    /// </summary>
    private Task DiscordClient_Log(LogMessage arg) {
        var msg = $"[{Enum.GetName(typeof(LogSeverity), arg.Severity)}] {arg.Message}";
        if (arg.Exception != null) msg += arg.Exception.ToString();

        switch (arg.Message) { // Prevent webhook logs for these 'important' Discord.Net messages
            case "Connecting":
            case "Connected":
            case "Ready":
            case "Disconnecting":
            case "Disconnected":
            case "Resumed previous session":
            case "Failed to resume previous session":
                break;
        }
        DoLog("Discord.Net", msg);

        return Task.CompletedTask;
    }

    // Hooked
    internal void DoLog(string source, string? message) {
        message ??= "(null)";
        var now = DateTimeOffset.Now;
        var output = new StringBuilder();
        var prefix = $"[{now:s}] [{source}] ";
        foreach (var line in message.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None)) {
            output.Append(prefix).AppendLine(line);
        }
        var outstr = output.ToString();
        Console.Write(outstr);
        var filename = _logBasePath + Path.DirectorySeparatorChar + $"{now:yyyy-MM}.log";
        File.AppendAllText(filename, outstr, Encoding.UTF8);
    }
}
{'fraction_non_alphanumeric': 0.11720798870284446, 'fraction_numerical': 0.00040346984062941297, 'mean_word_length': 2.4841883345045677, 'pattern_counts': {'":': 14, '<': 11, '<?xml version=': 0, '>': 11, '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': '4492752', 'n_tokens_mistral': 1337, 'n_tokens_neox': 1309, 'n_words': 364}