Как стать автором
Обновить
10
0
Роман Покровский @RomanPokrovskij

программист, фрилансер

Отправить сообщение
Почему в режиме отладки? Классы статичны их можно визуализировать и не в ран тайм. Означает ли это что вы вовсе не классы визуализируете а объекты?
Большое спасибо за вопроас! Здесь асинхронность в том что сообщения в логах просматриваются независимо не синхронно к процессу запроса (у вас человеком, но вообще это может и должно быть автоматизированно «собери мне все сообщения с этим токеном»). There is no confusion about which call produced given result — ключевое. У вас была confusion (и по хорошему остается пока недобавили GUID в HttpContext.Items). Вы ее решили забавным трюком, хотя по простому это решается тем что каждое сообщение в лог добавляется с correlation token'ом (который надо передавать конечно, между службами, между удаленными явно, между теми что получаются из DI — не явно вводя IoC container per request — там в IoC container е и будет жить метод лога который логит все время вставляя correlation token).
Можно просто поместить этот ваш correlation token в HttpContext.Items
— соглашусь.
В этом случае вам придется выдавать тот же самый идентификатор сессии,
— не соглашусь. correlation token может задаваться хоть в броузере, и потом передваться по всем серверам. не
придется выдавать тот же самый
— а цель такая использовать тот же самый по всему жизненному циклу запроса на всех серверах.

да, он часть интерфейса систем и может портить бизнес api используемый между системами (хотя не всегда, например в запросе от токена может передаваться через хидер, когда логика — через запрос). однако когда аудит — это действительно требование (а не нужда девелопера — трейсинга), он становится частью спецификации а значит вполне заслуживает упоминания в межсетевых интерфейсах.

вообще correlation token не мой books.google.lt/books?id=qqB7nrrna_sC&pg=PA165&lpg=PA165&dq=correlation+token+pattern&source=bl&ots=57keILj-x3&sig=kjU_rPtc_ouh6u5WVGkgBmkqGCM&hl=en&sa=X&ved=0ahUKEwiNv-_085HYAhVIJ1AKHRrICmoQ6AEIPDAD#v=onepage&q=correlation%20token%20pattern&f=false
Кроме того, вам придется изобрести велосипед механизм для добавления ваших сервисов в режиме «per session», поскольку ASP.NET Core такого режима не поддерживает.


А его не надо поддерживать в ASP.NET, точка входа — на например вызов action контроллера. Тут создал контейнер и далее «пропихиваешь его внутрь», через конструкторы сервисов. Тут нет никакого велосипеда чистое и ясное «функциональное» программирование если хотите. Вроде node'ы.
«Здоровый человек» — это мем (легкие курильщика), я не хотел вас напрячь, приношу извинения.

Как не странно мне вас убеждать что вы «не связали», но это именно так. По крайней мере для нужд разбора полетов «урл» недостаточно, нужна связь со всем запросом пользователя, по этому я и бился как рыба об лед: где тут связь с конкретным запросом, где тут коррелайшн токен. Use case разбора полетов: «клиент на запросе поймал ошибку — автоматом выслал коррелайшн токен своего запроса, бекофис сформировал тикет со всеми сообщениями из логов (всех логов, всех сервисов включая удаленные) для этого запроса (отобрав по коррелайшн токену)». В вашей архитектуре такого нет и легко реализовать не получится (где гибкость?). Моя оценка: не только архитектурное решение несовершенно, но и если это считается «а мы связали» — задача аудита понята не правильно.

Мы сейчас в клинче и наверное не нужно мне вещать о своем виденьи оно будет принято в штыки, но вы были внимательны к моим вопросам, и я должен ответить вам тем же.

Под контейнером я имел ввиду DI контейнер. DI контейнер не обязан быть глобальным и а может быть per session, и может вполне передаваться по стеку. Он не становится при этом service locator'ом поскольку service locator, по крайней мере тот который ругают, это глабальная переменная, тут же это просто «DI конейнер передаваемый по стеку». Это вообще абсолютно нормально — DI контейнер это «еще один сервис» — что тут такого где в архитектуре где «всё сервис». Ничто не мешает передававть IServiceCollection (DI контейнер) ИЛИ ЕГО ВРАПЕР в controller через constructor injection и уже из него запрашивать нужные сервисы, сервис выдающий сервисы.

Ни одна реализация сервиса при этом не будет зависить от других реализаций, но будут зависеть от контейенра.

Пример

public interface IContainer{
  IAuditService GetAuditService();
  IMyService GetMyService();
}

// можно и дженерик, строго говоря нет причин иметь универсальных дженерик контейнеры  вроде
//public interface IContainer<T>{
//  T GetService();
//}

public class Container: IContainer{
  public Container(Guid correlationToken){
  }
  public IAuditService GetAuditService(){ 
    return new AuditService(correlationToken)
  }
  public IMyService GetMyService(){ 
    return new MyService(GetAuditService()); // DI как он есть
  }
}

public interface IAuditService {
 void Log(string message);
}

public class AuditService: IAuditService {
  Guid correlationToken;
  public void AuditService(Guid correlationToken) =>  this.correlationToken=correlationToken;
  public void Log(string message)=> Console.WriteLine($" {correlationToken} "+ message);

}

public class MyService: IMyService{
    IAuditService auditService 
    MyService(IContainer container){
       this.auditService =container.GetAuditService();
      auditService.Log("bla bla"); // будет залогено вместе с correlation token
    }
}


По ассемблям/слоям: 1) IMyService, IAuditService 2) IContainer (ссылается на 1) 3) MyService, AuditService (ссылается на 1 и 2) 4) Container (ссылается на 1 2 3) — контейнер должен знать всех. При этом ASP.CORE проект линкуется только с 1) 2) 4) но не с три (не с реализациями сервисов).

Я чувствую что это плохо оформленно а плохо оформленное злит. Значит закончу тоже извинением.
Не понятны мне ваши аргументы.
1) разрешите повторить: есть реализация под react с 3000 звезд.
2) Angular это отдельная экосистема, бутсрап в ней не нужен (а и «не будут работать корректно» — спорно, зависит от качества директив обвязки).
3) мало контролов — бутсрап это о том как формализовать «пользовательский интерфейс в броузере», из каких частей состоит, как эти части взаимодействуют, средствами HTML5. минимализм — это не недостаток, это достоинство для любой формальной системы.
Вступление: это не ошибка конечно dataProvider в моем коде — transient.
А если сервис удаленный то и решение будет другим (без передачи делегата), а именно каждое аудит сообщение будет логится с нашим corellation tokenом (в нашем случае пойдет и тот самый GUID ожидания), по нему впоследствии всё и свяжем. Я не вижу тут никакой проблемы.

Но пусть будет сервис не удаленный, а работающий в нашем процессе, «доступный из контейнера».
Не просто не вижу проблемы в «придется прокидывать ваш AuditOnResult глубже и глубже» — а считаю возможность прокидывать (конечно не AuditOnResult, а контейнер созданный для конретной сессии с которого можно получить AuditOnResult) важнейшим признаком архитектуры здорового человека. Прокидывать не через interface кончено (т.е. уродовать API не будет) а через реализации.
Получив AuditOnResult инициированный correlationTokenом я буду логить весь аудит из сервиса вместе с correlationTokenом даже не зная значения correlationToken — поскольку в сервисе получаю лишь обертку созданную для конкретной сессии.

Но вернемся еще назад, я не понял как же вы связываете

2017-10-08 20:29:34.3322|0|AspNetCoreApp.Services.LongRespondingService|TRACE|Home/Test| DoJob method is ending (AspNetCoreApp.Services.LongRespondingService+d__3.MoveNext:0)| url:http://localhost/Test/1

с сообщениями из providerа. В этом вашем примерном сообщении нет ничго что может связать. Нет GUIDа. А возможности конфигурации NLOG не позволяют кидать все сообщения и контролера и провайдера «в файл per session». Или нет?

Больше у меня вопросов не будет.
Чуть чуть повторюсь. Вот ваш результат, вы им довольны.

2017-10-08 20:29:34.3322|0|AspNetCoreApp.Services.LongRespondingService|TRACE|Home/Test| DoJob method is ending (AspNetCoreApp.Services.LongRespondingService+d__3.MoveNext:0)| url:http://localhost/Test/1

Именно такое аудит сообщение — время, контейнер, акшн и url я могу послать на логгер явно в колбеке без всяких фокусов. В нем нет ничего более, чем в примере который предоставили вы. Вы согласны с этим? Это повторение вопроса.

Видимо вы не возвражаете, но начинаете говорить о том что надо «сопоставить сообщения из контроллера с сообщениями из провайдера».
Я могу предположить что под «сопоставить» вы хотите иметь законную возможность «каждую сессию — в отдельный файл (и сообщение MVC Controllerа и провайдера)» — это логично, но по скольку у вас у самого такого решения нет — то я зависаю.
Другое предоположение вы хотите иметь чтобы все сообщения одной сессии были залогены с каким-то общим correlation token'ом по которому можно их отобрать. Опять же у вас самого такого решения нет, опять зависаю.

Как бы я сам решал?

public class DefaultController : Controller
    {
        public IActionResult Index(Reqest request)
        {
            Guid correlationToken = Guid.NewGuid().ToString();
            logger.Log($"API starting {correlationToken}, {request}");
            var queryString = this.HttpContext.Request.GetDisplayUrl();
            Task.Factory.StartNew(
                    () =>  {
                                  dataProvider.AuditOnResult = (msg)=>{
                                                               logger.Log($"API finishing: {correlationToken}, {msg}")
                                                      };
                                  dataProvider.Start(request);
                                  logger.Log($"API finishing: {correlationToken}, {queryString}")
                               }
            );
             logger.Log("HTTP session finishing {correlationToken}");
            return Content(correlationToken); 
        }
    }


Наверное надо задать промежуточный вопрос: «а как вы сопоставляете данные, которые мы залогировали (displayUrl), с запросом к АПИ »? Из конфигурации форматера не видно. А если где и скзалаи, то у меня блокируются мозги уже потому что в форматере то и не видно, а сопоставлять вы ставите задачу именно им сформированное сообщение.

Я не понимаю потому что вижу кучу противоречий и мечусь между ними.

${longdate}|${event-properties:item=EventId.Id}|${logger}|${uppercase:${level}}|${aspnet-mvc-controller}/${aspnet-mvc-action}| ${message} (${callsite}:${callsite-linenumber})| url:${aspnet-request-url}

т.е.

2017-10-08 20:29:34.3322|0|AspNetCoreApp.Services.LongRespondingService|TRACE|Home/Test| DoJob method is ending (AspNetCoreApp.Services.LongRespondingService+d__3.MoveNext:0)| url:http://localhost/Test/1

А теперь возник httpresponce.body… Где в вашем решении боди?
А потом мне надо будет понять что такое «сопоставить данные» залогировать httpresponce.body вместе с сообщением finishing…? Так в body лежит GUID, я его и так явно залогировать могу… Для нужд аудита — достаточно. Залогировать что-то что вернет API вместе — ну какие проблемы? А что это в вашем примере? Да не важно переношу операцию логгинга прямо за операцией вызова (и удаляю «явный коллбек т.е. continueWith») — все будет работать. Любые поля из реквеста. А респонс — ну не знаю, что там нужно то тоже можно. Но я в вашем NLOG шаблоне не вижу нчи
Спасибо за ответы. Поскольку я фрилансер, мне сильно не хватает информации «как это у других» (точнее я вижу как у других — и сильно огорчаюсь — а получить разъяснения — вот
этого не получается совсем).

Вот более детальный код:

    public class DefaultController : Controller
    {
        public IActionResult Index()
        {
            var queryString = this.HttpContext.Request.GetDisplayUrl();
            Task.Factory.StartNew(
                    () =>  System.Threading.Thread.Sleep(4000)
                ).ContinueWith(
                    (notused) => Console.WriteLine($"API finishing: {queryString}")
            );
            Console.WriteLine("HTTP finishing");
            return Content(Guid.NewGuid().ToString()); 
        }
    }


Не понятно почему async await становится особенным условием — это же наш контроллер, если необходимо то ставим, если не обязательно — то не ставим. Я опущу просто потому что здесь не обязательно.

Ответ трейсера содержит
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 99.6118ms 200 text/plain; charset=utf-8
API finishing: localhost:50527/Default

вот есть информация о реквесте на коллбеке. что я упускаю?
«госудраство запретит» — государство не везде оккупационное, есть избиратели с реальными правами (или если вы коммунист — правящий класс, которому безопасность капиталов от посяганий бюрократии тоже важно). да и какая разница условной Финляндии — она и евро не контролирует — основой легализации биткоина вполне может быть тот же компромисс лежащий в основе евро.
Не понял дважды. Во первых у явного аудита — явный формат, и будет в сообщении поле «место вызова» — его не может не быть. Туда и положите контроллер с акшином. А не при МВЦ другой идентификатор места вызова.
Во-вторых колбек почему-то в «провайдер данных» уложили…
Давайте не сводить меня с ума и колбек вне провайдера оставим.

var controllerText = «myController»;
return Task.Factory.StartNew(
{
// API call
}
).ContinueWith(()=>{ logger.Log(«bla,bla» + controllerText )});

Извиняюсь за скороспелые выводы, но так работают мозги. Должна быть «рабочая гипотеза».

Теперь понял, вы просто нашли как NLOGу подсунуть частично клонутый HttpContext. Это выглядит в разы безопасней чем представлялось. Но на вопрос кто что об этом думает, отвечаю так же. Я думаю: «аудит логгить надо явно». Понимаю что готовое решение убедительней моего мнения (основанного на требованиях к гибкости решения — если регулярно используется — нужно будет регулярно улучшать).

Под «собрать строчки»я имел ввиду просто передать колбеку параметры со значениями нужными вам в логе ${aspnet-mvc-controller}/${aspnet-mvc-action} и т.п. В колбеке был бы явный вызов logger.Log в нужный вам таргет со всем этим добром.

А можно спросить еще, NLog уже умеет через appsettigns.json конфигурироваться?
Извините, не понял сразу. Спасибо за терпение.

Это было из разряда не укладывается в голове: не понятно зачем «логгировать детали запроса», если запрос повторяется? Я выбрал гипотезу «ну значит не повторяется».
Перечитал. Теперь понял что вы стартуете таск с колбеком и когда колбек возвращатся все нормально работает, но в логах нет всей нужной информации (конекст занулен). Поэтому… вы восстанавливаете контекст, чтобы LoggerProvider имел что залогить.

Тогда суть критики меняется: то что называется «данные для разбора полетов» — это аудит, бизнес логика, а не трейсинг. Для аудита не надо использовать LoggerProvider. Если бы у вас была возможность отослать аудит сообщение из колбека явно, у вас бы не было всей этой сомнительной мороки с восстановлением контекста. Надежность, утечки, скорость, GC? Вы бы в колбек передали нужные строчки и оттуда их и залоггили бы. Не обижайтесь, вы продемонстрировали серый трюк, на фоне неправильной архитектуры. Хотя приобретенным знаниям по глубинам ASP.CORE я завидую.

А такие замечательные гифки ловящие курсор и ДнД чем делаются?
Чтобы избавится от лишних перерисовок реализуем метод shouldComponentUpdate в GridDragLayer. Для плавности нам нужно обеспечить 60fps, т.е. одна перерисовка на 16 мс.
Возможные «залипания», когда компонент изменил свое состояние в интервале 16 мс, но не был перерисован, устраняются таймером и forceUpdate.


А что действительно нет возможности не перерисовывать весь GridDragLayer? Я правильно этому ужасаюсь, или «так и должно быть», «иначе не сделаешь»? Оно моргать не будет? А на планшетке? Я когда писал расписания с драгндропами с jquery-ui selectable/draggable/droppable (сотни объектов) никаких проблем с производительностью не имел и вообще не задумвывался о fps. Что-то я как то не готов морально к такому прогрессу. Ободрите, что все тут ok.
Спасибо за ответ.
Все остальное — ajax обертки и код "ждите" — остается ровным счетом таким же
Если это о клиентской части то разве в вашем решении клиент не просто держит соединение? В моем решении — крутит запросы циклом, пока получает «ответ ждите» (возможно с подсказкой сколько секунд).

Что касается ID — ну можно сгенерировать на стороннем сервере предварительно (а можно и сотню и закешировать). Задержка? Да, но это доля секунды, а вообще разговор же идет значительно медленном основном запросе (десятки секунд).

В общем раз вы говорите что разница между решениям не большая, просто в вашем надо разобраться с внутренностями ASP.CORE а в моем нет — меня такой уровень понимания устроит. Случайному читателю порекомендую не разбираться, не то чтобы asp.core не заслуживает изучения, задача просто в статье решается не прямым способом (прямым — это correlation-token по месту создания запроса, т.е на клиенте). Но конечно каждый выбирает по себе. Слабая сторона: я так никогда и не узнаю что-такое CallContext, и ExecutionContext, а может это что-то важное…
Спасибо. К сожалению эта книга 2013 года и не то чтобы устрарела, но не убеждает в необходимости спускаться на уровень NumPy, pandas, matplotlib… По крайней мере в «современной книге» хотелось бы найти что вот такое представление возможно: тут graphite — запрашивай данные, трансформируй (мерж, апроксимация, фильтрация, выравнивание), вот тут graphene django публикуй данные (с возможностью drill in), а где-то по середине еще и кэш.
А есть ли книга для начинающих с упором на graphene, graphene-django, time series analysis, graphite?

Информация

В рейтинге
Не участвует
Откуда
Вильнюс, Литва, Литва
Зарегистрирован
Активность