آکادمی برای روزلین
مقدمه
ایجاد کلاس لاگ
public class DBLog { public int DBLogId { get; set; } public string? LogLevel { get; set; } public string? EventName { get; set; } public string? Message { get; set; } public string? StackTrace { get; set; } public DateTime CreatedDate { get; set; }=DateTime.Now; }
ایجاد دیتابیس لاگر
public class DBLogger:ILogger { private bool _isDisposed; private readonly ApplicationDbContext _dbContext; public DBLogger(ApplicationDbContext dbContext) { _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); } public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) { var dblLogItem = new DBLog() { EventName = eventId.Name, LogLevel = logLevel.ToString(), Message = exception?.Message, StackTrace=exception?.StackTrace }; _dbContext.DBLogs.Add(dblLogItem); _dbContext.SaveChanges(); } public bool IsEnabled(LogLevel logLevel) { return true; } public IDisposable BeginScope<TState>(TState state) { return null; } }
ایجاد یک لاگ پروایدر سفارشی
حال باید یک لاگ پروایدر سفارشی را ایجاد کنیم تا بتوان یک نمونه از دیتابیس لاگر سفارشی بالا (DBLogger) را ایجاد کرد.
public class DbLoggerProvider:ILoggerProvider { private bool _isDisposed; private readonly ApplicationDbContext _dbContext; public DbLoggerProvider(ApplicationDbContext dbContext) { _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); } public ILogger CreateLogger(string categoryName) { return new DBLogger(_dbContext); } public void Dispose() { } }
همانطور که ملاحظه مینمایید، این لاگ پروایدر، از اینترفیس ILoggerProvider ارث بری کردهاست که دارای متد CreateLogger میباشد ئ این متد با شروع برنامه، یک نمونه از دیتابیس لاگر سفارشی ما را ایجاد میکند. در سازندهی این کلاس، DatabaseContext را مقدار دهی نمودهایم تا آنرا به کلاس DBLogger ارسال نماییم.
در انتها کافیست در کلاس Startup.cs این لاگ پروایدر سفارشی (DbLoggerProvider ) را صدا بزنیم.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) { . . . #region CustomLogProvider var serviceProvider = app.ApplicationServices.CreateScope().ServiceProvider; var appDbContext = serviceProvider.GetRequiredService<ApplicationDbContext>(); loggerFactory.AddProvider(new DbLoggerProvider(appDbContext)); #endregion . . .
مشکل!
public class DbLoggerProvider:ILoggerProvider { private readonly CancellationTokenSource _cancellationTokenSource = new(); private readonly IList<DBLog> _currentBatch = new List<DBLog>(); private readonly TimeSpan _interval = TimeSpan.FromSeconds(2); private readonly BlockingCollection<DBLog> _messageQueue = new(new ConcurrentQueue<DBLog>()); private readonly Task _outputTask; private readonly ApplicationDbContext _dbContext; private bool _isDisposed; public DbLoggerProvider(ApplicationDbContext dbContext) { _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); _outputTask = Task.Run(ProcessLogQueue); } public ILogger CreateLogger(string categoryName) { return new DBLogger(this,categoryName); } private async Task ProcessLogQueue() { while (!_cancellationTokenSource.IsCancellationRequested) { while (_messageQueue.TryTake(out var message)) { try { _currentBatch.Add(message); } catch { //cancellation token canceled or CompleteAdding called } } await SaveLogItemsAsync(_currentBatch, _cancellationTokenSource.Token); _currentBatch.Clear(); await Task.Delay(_interval, _cancellationTokenSource.Token); } } internal void AddLogItem(DBLog appLogItem) { if (!_messageQueue.IsAddingCompleted) { _messageQueue.Add(appLogItem, _cancellationTokenSource.Token); } } private async Task SaveLogItemsAsync(IList<DBLog> items, CancellationToken cancellationToken) { try { if (!items.Any()) { return; } // We need a separate context for the logger to call its SaveChanges several times, // without using the current request's context and changing its internal state. foreach (var item in items) { var addedEntry = _dbContext.DbLogs.Add(item); } await _dbContext.SaveChangesAsync(cancellationToken); } catch { // don't throw exceptions from logger } } [SuppressMessage("Microsoft.Usage", "CA1031:catch a more specific allowed exception type, or rethrow the exception", Justification = "don't throw exceptions from logger")] private void Stop() { _cancellationTokenSource.Cancel(); _messageQueue.CompleteAdding(); try { _outputTask.Wait(_interval); } catch { // don't throw exceptions from logger } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_isDisposed) { try { if (disposing) { Stop(); _messageQueue.Dispose(); _cancellationTokenSource.Dispose(); _dbContext.Dispose(); } } finally { _isDisposed = true; } } } }
public class DBLogger:ILogger { private readonly LogLevel _minLevel; private readonly DbLoggerProvider _loggerProvider; private readonly string _categoryName; public DBLogger( DbLoggerProvider loggerProvider, string categoryName ) { _loggerProvider= loggerProvider ?? throw new ArgumentNullException(nameof(loggerProvider)); _categoryName= categoryName; } public IDisposable BeginScope<TState>(TState state) { return new NoopDisposable(); } public bool IsEnabled(LogLevel logLevel) { return logLevel >= _minLevel; } public void Log<TState>( LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { if (!IsEnabled(logLevel)) { return; } if (formatter == null) { throw new ArgumentNullException(nameof(formatter)); } var message = formatter(state, exception); if (exception != null) { message = $"{message}{Environment.NewLine}{exception}"; } if (string.IsNullOrEmpty(message)) { return; } var dblLogItem = new DBLog() { EventName = eventId.Name, LogLevel = logLevel.ToString(), Message = $"{_categoryName}{Environment.NewLine}{message}", StackTrace=exception?.StackTrace }; _loggerProvider.AddLogItem(dblLogItem); } private class NoopDisposable : IDisposable { public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { } } }
استخراج اطلاعات از Microsoft Band
Build Accelerator, BuildXL for short, is a build engine originally developed for large internal teams at Microsoft, and owned by the Tools for Software Engineers team, part of the Microsoft One Engineering System internal engineering group. Internally at Microsoft, BuildXL runs 30,000+ builds per day on monorepo codebases up to a half-terabyte in size with a half-million process executions per build, using distribution to thousands of datacenter machines and petabytes of source code, package, and build output caching. Thousands of developers use BuildXL on their desktops for faster builds even on mega-sized codebases.
ایجاد Pull Requests از طریق VSCode
- C++ Linux project - Remote header sync is broken in Visual Studio 16.3
- Visual Studio 16.3 opens some files with notepad
- Fixed an issue with the Show Output window either closing too quickly.
- Fixed an issue where Visual Studio 2019 stops responding in several scenarios, including opening a solution, changing solution configuration, and closing a solution.