Как увеличить/уменьшить отступ при входе/выходе из методов?

TL;DR - перейти к последнему абзацу

Фон

Я выполняю некоторые тесты на основе данных и использую файл журнала в качестве одного из результатов тестирования. Это работает примерно так-

  • Read first file in folder
    • Process first line and convert to a test
    • Run Test
      • Perform Validation 1
      • ...
    • ...
  • Читать следующий файл
  • И т.п.

Мой файл журнала отражает это:

INFO - Start RunAllFilesInFolder  
INFO - File1:
INFO -      Some info
INFO -      Executing Test 1  
INFO -          Validation A result
INFO -          ...
INFO -      ...  
INFO - File2:  
...

На данный момент я использую/вызываю сеть Log 4 следующим образом:

static class LogHelper
{
    internal static readonly log4net.ILog Log = log4net.LogManager.GetLogger
            (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
}

// another file
using static Some.Namespace.LogHelper;
class SomeClass
{
     Log.Info($"\t\tExecuting {t.Number}-{t.Name}");
}

Где отступ = "\t", "\t\t" или "\t\t\t" в зависимости от уровня, на котором находится тест.


Есть ли способ реорганизовать вызов LogHelper.Log, чтобы он учитывал статический «отступ», который можно увеличивать/уменьшать при переходе на разные уровни тестирования без явного указания отступа в каждом вызове?

т.е. Уметь вызывать, где это применимо, что-то вроде

Log.Indent.Increase();

Or

Log.Indent.Decrease();

И замените приведенный выше вызов для журнала просто на -

Log.Info($"Executing {t.Number}-{t.Name}");

person Ory Zaidenvorm    schedule 24.03.2016    source источник


Ответы (1)


Вы можете использовать трассировку стека length, подсчитав количество новых строк:

int someSensibleMinimum = 3; //something that works for you
int count = Environment.StackTrace.Count(a => a=='\n');
var indent = new string('\t', Math.Max(0, count - someSensibleMinimum));

Обратите внимание, что в Release это может вести себя по-другому:

Однако свойство StackTrace может не сообщать столько вызовов методов, сколько ожидалось, из-за преобразований кода, происходящих во время оптимизации.

Или вы можете автоматически вычислить длину, используя этот подход (псевдокод):

int count = Environment.StackTrace.Count(a => a=='\n');
Look for stack length in dictionary<int,string> (length to indent string)
If found use it.
If not found, find the largest entry in dictionary where key < count
add new entry to dictionary one tab char longer

В коде:

public sealed class IndentTracker
{
    private readonly ThreadLocal<Dictionary<int, string>> _dictionaryLocal =
        new ThreadLocal<Dictionary<int, string>>(() => new Dictionary<int, string>());

    public string GetIndent()
    {
        Dictionary<int, string> dictionary = _dictionaryLocal.Value;

        int count = Environment.StackTrace.Count(a => a == '\n');

        if (!dictionary.Any())
        {
            string initialIndent = string.Empty;
            dictionary.Add(count, initialIndent);
            return initialIndent;
        }

        string indent;
        if (dictionary.TryGetValue(count, out indent))
            return indent;

        string last = dictionary.OrderByDescending(k => k.Key).First(k => k.Key < count).Value;
        string newIndent = last + '\t';
        dictionary.Add(count, newIndent);
        return newIndent;
    }
}

Это работает при входе в порядке возрастания глубины. Например, произойдет сбой, если вы регистрируетесь на глубине стека 10, а затем на 5 (без предварительной регистрации на глубине стека 5, а затем 10).

person weston    schedule 24.03.2016
comment
Спасибо за подробный ответ! К сожалению, на данном этапе это может быть излишним для моих целей... Не могли бы вы попробовать ответить на конкретный вопрос (последний абзац), используя свой класс IndentTracker? - person Ory Zaidenvorm; 29.03.2016
comment
Я отредактировал вопрос, чтобы было понятнее, что именно мне нужно - person Ory Zaidenvorm; 29.03.2016
comment
Сейчас звучит очень просто. Не уверен, где у вас возникли трудности с реализацией этого самостоятельно. - person weston; 29.03.2016
comment
Я не проверял это, но я думаю, что использование Environment.StackTrace довольно дорого с точки зрения вычислений. если вы делаете сотни журналов в секунду, это может оказать значительное влияние на производительность. - person Andy; 18.01.2018
comment
@ И у него линейная временная сложность, если вы удвоите количество журналов, это займет вдвое больше времени. Это дороже, чем без отступов, конечно! несу ли я ответственность за предоставление микрооптимизированного решения? - person weston; 18.01.2018
comment
@weston Я понимаю, что у него есть линейная сложность, но я всегда понимал, что создание трассировки стека довольно дорого, поэтому я решил провести тест. На моем ноутбуке (YMMV) вызов Environment.StackTrace.Count(...) занимает около 0,5 мс с глубиной стека 30. Вызов log.Debug (запись на локальный диск) также занимает около 0,5 мс, поэтому он выглядит как добавление это может удвоить время, необходимое для ведения журнала. Это может быть значительным, но на самом деле это гораздо меньшее влияние, чем я думал. - person Andy; 19.01.2018
comment
@Энди Кул, спасибо за проверку. Моя мысль по этому поводу такова: если вы делаете много журналов во время критического по времени кода, и вы также полагаетесь на красивые отступы, то у вас уже проблемы, хуже от этого не станет! - person weston; 20.01.2018